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