1 // license:BSD-3-Clause
2 // copyright-holders:Robbbert
3 /********************************************************************
4
5 Support for Microbee cassette images
6
7 Microbee tapes consist of 3 sections
8 1. A leader of 63 zeroes
9 2. A header which contains the program name and other info
10 3. The main program
11
12 Each byte after conversion becomes a start bit, bit 0,1,etc to 7,
13 then 2 stop bits.
14
15 At 1200 baud, a high = 2 cycles of 2400Hz and a low = 1 cycle of 1200Hz
16 At 300 baud, a high = 8 cycles of 2400Hz and a low = 4 cycles of 1200Hz
17
18 The header bytes are arranged thus:
19 1 (SOH) 0x01
20 6 File name
21 1 file type (M=machine language, B=Basic)
22 2 length
23 2 load address
24 2 exec address
25 1 tape speed (0 = 300 baud; other = 1200 baud)
26 1 auto-start (0 = no)
27 1 unassigned byte
28 1 CRC byte
29
30 The header is always at 300 baud; the program will be at the
31 speed indicated by the speed byte.
32
33 By coincidence (or not), the header is the same format as that
34 of the Sorcerer and SOL-20. In these, the speed and auto-start
35 bytes are unassigned. The CRC uses the same algorithm.
36
37 The main program is broken into blocks of 256, with each block
38 having its own CRC byte.
39
40 Microbee tape and quickload formats:
41
42 BEE - straight binary dump to address 0900, no header. For Machine
43 Language programs.
44
45 BIN - the standard z80bin format.
46
47 COM - straight binary dump to address 0100, no header. For Machine
48 Language programs.
49
50 MWB - straight binary dump to address 08C0, no header. For BASIC
51 programs.
52
53 TAP - has an ID header of TAP_DGOS_BEE or MBEE, null terminated.
54 This is followed by the binary dump with the leader and CRC
55 bytes included.
56
57 ********************************************************************/
58
59 #include <cassert>
60
61 #include "mbee_cas.h"
62
63 #define WAVEENTRY_LOW -32768
64 #define WAVEENTRY_HIGH 32767
65
66 #define MBEE_WAV_FREQUENCY 9600
67
68 // image size
69 static int mbee_image_size;
70 static bool mbee_speed;
71
mbee_put_samples(int16_t * buffer,int sample_pos,int count,int level)72 static int mbee_put_samples(int16_t *buffer, int sample_pos, int count, int level)
73 {
74 if (buffer)
75 {
76 for (int i=0; i<count; i++)
77 buffer[sample_pos + i] = level;
78 }
79
80 return count;
81 }
82
mbee_output_bit(int16_t * buffer,int sample_pos,bool bit)83 static int mbee_output_bit(int16_t *buffer, int sample_pos, bool bit)
84 {
85 int samples = 0;
86 if (mbee_speed)
87 {
88 if (bit)
89 {
90 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
91 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
92 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
93 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
94 }
95 else
96 {
97 samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_LOW);
98 samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_HIGH);
99 }
100 }
101 else
102 {
103 if (bit)
104 {
105 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
106 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
107 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
108 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
109 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
110 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
111 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
112 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
113 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
114 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
115 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
116 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
117 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
118 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
119 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_LOW);
120 samples += mbee_put_samples(buffer, sample_pos + samples, 2, WAVEENTRY_HIGH);
121 }
122 else
123 {
124 samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_LOW);
125 samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_HIGH);
126 samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_LOW);
127 samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_HIGH);
128 samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_LOW);
129 samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_HIGH);
130 samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_LOW);
131 samples += mbee_put_samples(buffer, sample_pos + samples, 4, WAVEENTRY_HIGH);
132 }
133 }
134
135 return samples;
136 }
137
mbee_output_byte(int16_t * buffer,int sample_pos,uint8_t byte)138 static int mbee_output_byte(int16_t *buffer, int sample_pos, uint8_t byte)
139 {
140 int samples = 0;
141 uint8_t i;
142
143 /* start */
144 samples += mbee_output_bit (buffer, sample_pos + samples, 0);
145
146 /* data */
147 for (i = 0; i<8; i++)
148 samples += mbee_output_bit (buffer, sample_pos + samples, (byte >> i) & 1);
149
150 /* stop */
151 for (i = 0; i<2; i++)
152 samples += mbee_output_bit (buffer, sample_pos + samples, 1);
153
154 return samples;
155 }
156
mbee_handle_tap(int16_t * buffer,const uint8_t * bytes)157 static int mbee_handle_tap(int16_t *buffer, const uint8_t *bytes)
158 {
159 uint32_t sample_count = 0;
160 uint32_t byte_count = 0;
161 uint32_t i = 0;
162 bool temp_speed = 0;
163 uint8_t temp_blocks = 0;
164 uint16_t temp_size = 0;
165
166 // TAP file starts with a null-terminate ID string. We just skip this.
167 while (bytes[byte_count])
168 byte_count++;
169
170 // there can be a library of files, loop through them all
171 while (byte_count < mbee_image_size)
172 {
173 mbee_speed = 0;
174
175 // now output the leader
176 while ( (!bytes[byte_count]) && (byte_count < mbee_image_size) )
177 sample_count += mbee_output_byte(buffer, sample_count, bytes[byte_count++]);
178
179 // make sure SOH is where we expect
180 if (bytes[byte_count] != 1 )
181 break;
182
183 // store the size for later
184 temp_blocks = bytes[byte_count + 9];
185 temp_size = bytes[byte_count + 8] + temp_blocks*256;
186
187 // store the speed for later
188 temp_speed = (bytes[byte_count + 15]) ? 1 : 0;
189
190 /* header */
191 for (i=0; i<18; i++)
192 sample_count += mbee_output_byte(buffer, sample_count, bytes[byte_count++]);
193
194 // change speed
195 mbee_speed = temp_speed;
196
197 // calculate size of program including CRC bytes
198 temp_size = temp_size + temp_blocks + 1;
199
200 /* data */
201 for (i=0; i<temp_size; i++)
202 sample_count += mbee_output_byte(buffer, sample_count, bytes[byte_count++]);
203
204 }
205
206 return sample_count;
207 }
208
209
210 /*******************************************************************
211 Generate samples for the tape image
212 ********************************************************************/
213
mbee_tap_fill_wave(int16_t * buffer,int length,uint8_t * bytes)214 static int mbee_tap_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
215 {
216 return mbee_handle_tap(buffer, bytes);
217 }
218
219 /*******************************************************************
220 Calculate the number of samples needed for this tape image
221 ********************************************************************/
222
mbee_tap_calculate_size_in_samples(const uint8_t * bytes,int length)223 static int mbee_tap_calculate_size_in_samples(const uint8_t *bytes, int length)
224 {
225 mbee_image_size = length;
226
227 return mbee_handle_tap(nullptr, bytes);
228 }
229
230 static const cassette_image::LegacyWaveFiller mbee_tap_config =
231 {
232 mbee_tap_fill_wave, /* fill_wave */
233 -1, /* chunk_size */
234 0, /* chunk_samples */
235 mbee_tap_calculate_size_in_samples, /* chunk_sample_calc */
236 MBEE_WAV_FREQUENCY, /* sample_frequency */
237 0, /* header_samples */
238 0 /* trailer_samples */
239 };
240
mbee_tap_identify(cassette_image * cassette,cassette_image::Options * opts)241 static cassette_image::error mbee_tap_identify(cassette_image *cassette, cassette_image::Options *opts)
242 {
243 return cassette->legacy_identify(opts, &mbee_tap_config);
244 }
245
mbee_tap_load(cassette_image * cassette)246 static cassette_image::error mbee_tap_load(cassette_image *cassette)
247 {
248 return cassette->legacy_construct(&mbee_tap_config);
249 }
250
251 static const cassette_image::Format mbee_tap_image_format =
252 {
253 "tap",
254 mbee_tap_identify,
255 mbee_tap_load,
256 nullptr
257 };
258
259 CASSETTE_FORMATLIST_START(mbee_cassette_formats)
260 CASSETTE_FORMAT(mbee_tap_image_format)
261 CASSETTE_FORMATLIST_END
262