1 // license:BSD-3-Clause
2 // copyright-holders: F. Ulivi
3 /*********************************************************************
4 
5     img_dsk.cpp
6 
7     "IMG" disk format
8 
9     This format is just a raw image of every sector on SSDD 8" floppy
10     disks as used in Intel MDS-II systems.
11     Files with this format have no header/trailer and are exactly
12     512512 bytes in size (52 sectors, 1 head, 77 tracks,
13     128 bytes per sector).
14 
15 *********************************************************************/
16 
17 #include "img_dsk.h"
18 
19 #include "coretmpl.h" // BIT
20 
21 
22 // Debugging
23 #define VERBOSE 0
24 #define LOG(...)  do { if (VERBOSE) osd_printf_info(__VA_ARGS__); } while (false)
25 
26 constexpr unsigned CELL_SIZE    = 1200; // Bit cell size (1 µs)
27 constexpr uint8_t  INDEX_AM     = 0x0c; // Index address mark
28 constexpr uint8_t  ID_AM        = 0x0e; // ID address mark
29 constexpr uint8_t  DATA_AM      = 0x0b; // Data address mark
30 constexpr uint8_t  AM_CLOCK     = 0x70; // Clock pattern of AM
31 constexpr unsigned PREIDX_GAP   = 45;   // Size of pre-index gap
32 constexpr unsigned SYNC_00_LEN  = 18;   // 00's in sync (gaps 1, 2, 3)
33 constexpr unsigned SYNC_FF_LEN  = 10;   // FF's in sync (gaps 1, 2, 3)
34 constexpr int ID_DATA_OFFSET    = 35 * 16;  // Nominal distance (in cells) between ID & DATA AM
35 // Size of image file
36 constexpr unsigned IMG_IMAGE_SIZE = img_format::TRACKS * img_format::HEADS * img_format::SECTORS * img_format::SECTOR_SIZE;
37 constexpr uint16_t CRC_POLY     = 0x1021;   // CRC-CCITT
38 
img_format()39 img_format::img_format()
40 {
41 }
42 
identify(io_generic * io,uint32_t form_factor)43 int img_format::identify(io_generic *io, uint32_t form_factor)
44 {
45 	uint64_t size = io_generic_size(io);
46 
47 	if (((form_factor == floppy_image::FF_8) || (form_factor == floppy_image::FF_UNKNOWN)) &&
48 		size == IMG_IMAGE_SIZE) {
49 		return 50;
50 	} else {
51 		return 0;
52 	}
53 }
54 
load(io_generic * io,uint32_t form_factor,floppy_image * image)55 bool img_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
56 {
57 	uint64_t size = io_generic_size(io);
58 	if (size != IMG_IMAGE_SIZE) {
59 		return false;
60 	}
61 	image->set_variant(floppy_image::SSDD);
62 
63 	// Suck in the whole image
64 	std::vector<uint8_t> image_data(size);
65 	io_generic_read(io, (void *)image_data.data(), 0, size);
66 
67 	for (unsigned cyl = 0; cyl < TRACKS; cyl++) {
68 		std::vector<uint32_t> track_data;
69 
70 		write_gap(track_data, 0 , PREIDX_GAP);
71 		write_mmfm_byte(track_data , INDEX_AM , AM_CLOCK);
72 
73 		// Compute interleave factor and skew for current track
74 		unsigned il_factor;
75 		unsigned skew;
76 		if (cyl == 0) {
77 			il_factor = 1;
78 			skew = 51;
79 		} else if (cyl == 1) {
80 			il_factor = 4;
81 			skew = 48;
82 		} else {
83 			il_factor = 5;
84 			skew = (47 + 45 * (cyl - 2)) % 52;
85 		}
86 		std::vector<uint8_t> sector_list = interleaved_sectors(il_factor);
87 
88 		for (unsigned sector = 0; sector < SECTORS; sector++) {
89 			unsigned real_sector = sector_list[ (sector + skew) % SECTORS ];
90 			unsigned offset_in_image = (real_sector - 1 + cyl * SECTORS) * SECTOR_SIZE;
91 			write_sector(track_data , cyl , real_sector , &image_data[ offset_in_image ]);
92 		}
93 		fill_with_gap4(track_data);
94 		generate_track_from_levels(cyl , 0 , track_data , 0 , image);
95 	}
96 	return true;
97 }
98 
save(io_generic * io,floppy_image * image)99 bool img_format::save(io_generic *io, floppy_image *image)
100 {
101 	for (int cyl = 0; cyl < TRACKS; cyl++) {
102 		uint8_t bitstream[ 21000 ];
103 		int bitstream_size;
104 		generate_bitstream_from_track(cyl , 0 , CELL_SIZE , bitstream , bitstream_size , image , 0);
105 		int pos = 0;
106 		unsigned track_no , sector_no;
107 		uint8_t sector_data[ SECTOR_SIZE ];
108 		while (get_next_sector(bitstream , bitstream_size , pos , track_no , sector_no , sector_data)) {
109 			if (track_no == cyl && sector_no >= 1 && sector_no <= SECTORS) {
110 				unsigned offset_in_image = (cyl * SECTORS + sector_no - 1) * SECTOR_SIZE;
111 				io_generic_write(io, sector_data, offset_in_image, SECTOR_SIZE);
112 			}
113 		}
114 	}
115 	return true;
116 }
117 
name() const118 const char *img_format::name() const
119 {
120 	return "img";
121 }
122 
description() const123 const char *img_format::description() const
124 {
125 	return "MDS-II floppy disk image";
126 }
127 
extensions() const128 const char *img_format::extensions() const
129 {
130 	return "img";
131 }
132 
supports_save() const133 bool img_format::supports_save() const
134 {
135 	return true;
136 }
137 
interleaved_sectors(unsigned il_factor)138 std::vector<uint8_t> img_format::interleaved_sectors(unsigned il_factor)
139 {
140 	std::vector<uint8_t> out(SECTORS);
141 	unsigned idx = 0;
142 	for (unsigned s = 0; s < SECTORS; s++) {
143 		while (out[ idx ] != 0) {
144 			if (++idx >= SECTORS) {
145 				idx = 0;
146 			}
147 		}
148 		out[ idx ] = s + 1;
149 		idx += il_factor;
150 		if (idx >= SECTORS) {
151 			idx -= SECTORS;
152 		}
153 	}
154 	return out;
155 }
156 
write_mmfm_bit(std::vector<uint32_t> & buffer,bool data_bit,bool clock_bit)157 void img_format::write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit)
158 {
159 	bool had_transition = buffer.size() < 2 ? false : bit_r(buffer, buffer.size() - 1) || bit_r(buffer , buffer.size() - 2);
160 	clock_bit = !data_bit && (clock_bit || !had_transition);
161 	bit_w(buffer , clock_bit , CELL_SIZE);
162 	bit_w(buffer , data_bit , CELL_SIZE);
163 
164 	if (util::BIT(m_crc , 15) ^ data_bit) {
165 		m_crc = (m_crc << 1) ^ CRC_POLY;
166 	} else {
167 		m_crc <<= 1;
168 	}
169 }
170 
write_mmfm_byte(std::vector<uint32_t> & buffer,uint8_t data,uint8_t clock)171 void img_format::write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint8_t clock)
172 {
173 	for (int i = 7; i >= 0; i--) {
174 		write_mmfm_bit(buffer , util::BIT(data , i) , util::BIT(clock , i));
175 	}
176 }
177 
write_sync(std::vector<uint32_t> & buffer)178 void img_format::write_sync(std::vector<uint32_t> &buffer)
179 {
180 	write_gap(buffer , SYNC_00_LEN , SYNC_FF_LEN);
181 }
182 
write_crc(std::vector<uint32_t> & buffer,uint16_t crc)183 void img_format::write_crc(std::vector<uint32_t> &buffer , uint16_t crc)
184 {
185 	// Note that CRC is stored with MSB (x^15) first
186 	for (unsigned i = 0; i < 16; i++) {
187 		write_mmfm_bit(buffer , util::BIT(crc , 15 - i) , 0);
188 	}
189 }
190 
write_gap(std::vector<uint32_t> & buffer,unsigned size_00,unsigned size_ff)191 void img_format::write_gap(std::vector<uint32_t> &buffer , unsigned size_00 , unsigned size_ff)
192 {
193 	for (unsigned i = 0; i < size_00; ++i) {
194 		write_mmfm_byte(buffer, 0);
195 	}
196 	for (unsigned i = 0; i < size_ff; ++i) {
197 		write_mmfm_byte(buffer, 0xff);
198 	}
199 }
200 
write_sector(std::vector<uint32_t> & buffer,uint8_t track_no,uint8_t sect_no,const uint8_t * sect_data)201 void img_format::write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_no , const uint8_t *sect_data)
202 {
203 	// **** On-disk format of a sector ****
204 	//
205 	// | Offset | Size | Value | Content              |
206 	// |--------+------+-------+----------------------|
207 	// |      0 |   18 |    00 | Gap 1/3              |
208 	// |     18 |   10 |    ff | Gap 1/3              |
209 	// |     28 |    1 |    0e | ID AM (clock = 70)   |
210 	// |     29 |    1 |    xx | Track no.            |
211 	// |     30 |    1 |    00 | N/U                  |
212 	// |     31 |    1 |    xx | Sector no.           |
213 	// |     32 |    1 |    00 | N/U                  |
214 	// |     33 |    2 |    xx | ID CRC               |
215 	// |     35 |   18 |    00 | Gap 2                |
216 	// |     53 |   10 |    ff | Gap 2                |
217 	// |     63 |    1 |    0b | Data AM (clock = 70) |
218 	// |     64 |  128 |    xx | Sector data          |
219 	// |    192 |    2 |    xx | Data CRC             |
220 	// |    194 |      |       |                      |
221 
222 	// Gap1
223 	write_sync(buffer);
224 	// ID AM
225 	m_crc = 0;
226 	write_mmfm_byte(buffer , ID_AM , AM_CLOCK);
227 	// Track #
228 	write_mmfm_byte(buffer , track_no);
229 	write_mmfm_byte(buffer , 0);
230 	// Sector #
231 	write_mmfm_byte(buffer , sect_no);
232 	write_mmfm_byte(buffer , 0);
233 	// ID CRC
234 	write_crc(buffer , m_crc);
235 	// Gap 2
236 	write_sync(buffer);
237 	// Data AM
238 	m_crc = 0;
239 	write_mmfm_byte(buffer , DATA_AM , AM_CLOCK);
240 	for (unsigned i = 0; i < SECTOR_SIZE; i++) {
241 		// Data
242 		write_mmfm_byte(buffer , sect_data[ i ]);
243 	}
244 	// Data CRC
245 	write_crc(buffer , m_crc);
246 }
247 
fill_with_gap4(std::vector<uint32_t> & buffer)248 void img_format::fill_with_gap4(std::vector<uint32_t> &buffer)
249 {
250 	// Cell count in a track (1 µs cells in a 1/6 s track)
251 	unsigned cell_count = (500000 * 120) / 360;
252 	unsigned cells_in_buffer = buffer.size();
253 	// Size of gap 4
254 	unsigned gap_4 = (cell_count - cells_in_buffer) / 16;
255 	// Gap 4
256 	write_gap(buffer , gap_4 , 0);
257 	// Last cell to round everything up to 2E+8
258 	if (buffer.size() * CELL_SIZE < 200000000) {
259 		bit_w(buffer , false , 200000000 - buffer.size() * CELL_SIZE);
260 	}
261 }
262 
get_next_id_n_block(const uint8_t * bitstream,int bitstream_size,int & pos,int & start_pos)263 std::vector<uint8_t> img_format::get_next_id_n_block(const uint8_t *bitstream , int bitstream_size , int& pos , int& start_pos)
264 {
265 	std::vector<uint8_t> res;
266 	uint8_t data_sr;
267 	uint8_t clock_sr;
268 
269 	// Look for either sync + ID AM or sync + DATA AM
270 	do {
271 		unsigned cnt_trans = 0;
272 		while (pos < bitstream_size && cnt_trans < 34) {
273 			bool bit = util::BIT(bitstream[ pos >> 3 ] , ~pos & 7);
274 			pos++;
275 			if (cnt_trans < 32) {
276 				if (!(util::BIT(cnt_trans , 0) ^ bit)) {
277 					cnt_trans++;
278 				} else {
279 					cnt_trans = 0;
280 				}
281 			} else if (cnt_trans == 32) {
282 				if (!bit) {
283 					start_pos = pos;
284 					cnt_trans++;
285 				} else {
286 					cnt_trans = 0;
287 				}
288 			} else if (!bit) {
289 				cnt_trans++;
290 			} else {
291 				cnt_trans = 32;
292 			}
293 		}
294 
295 		if (pos == bitstream_size) {
296 			// End of track reached
297 			return res;
298 		}
299 
300 		// Get AM
301 		data_sr = clock_sr = 0;
302 		for (unsigned i = 0; i < 7; ++i) {
303 			bool bit = util::BIT(bitstream[ pos >> 3 ] , ~pos & 7);
304 			pos++;
305 			clock_sr = (clock_sr << 1) | bit;
306 			bit = util::BIT(bitstream[ pos >> 3 ] , ~pos & 7);
307 			pos++;
308 			data_sr = (data_sr << 1) | bit;
309 		}
310 		if (pos >= bitstream_size) {
311 			return res;
312 		}
313 	} while (clock_sr != AM_CLOCK);
314 
315 	// ID blocks: Track no. + 0 + sector no. + 0 + CRC
316 	// Data blocks: Sector data + CRC
317 	res.push_back(data_sr);
318 	unsigned to_dump;
319 	if (data_sr == ID_AM) {
320 		to_dump = 4;
321 	} else if (data_sr == DATA_AM) {
322 		to_dump = SECTOR_SIZE;
323 	} else {
324 		to_dump = 0;
325 	}
326 
327 	pos++;
328 	for (unsigned i = 0; i < to_dump && pos < bitstream_size; i++) {
329 		data_sr = 0;
330 		unsigned j;
331 		for (j = 0; j < 8 && pos < bitstream_size; j++) {
332 			bool bit = util::BIT(bitstream[ pos >> 3 ] , ~pos & 7);
333 			pos += 2;
334 			data_sr = (data_sr << 1) | bit;
335 		}
336 		if (j == 8) {
337 			res.push_back(data_sr);
338 		}
339 	}
340 	return res;
341 }
342 
get_next_sector(const uint8_t * bitstream,int bitstream_size,int & pos,unsigned & track,unsigned & sector,uint8_t * sector_data)343 bool img_format::get_next_sector(const uint8_t *bitstream , int bitstream_size , int& pos , unsigned& track , unsigned& sector , uint8_t *sector_data)
344 {
345 	std::vector<uint8_t> block;
346 	while (true) {
347 		// Scan for ID block first
348 		int id_pos = 0;
349 		while (true) {
350 			if (block.size() == 0) {
351 				block = get_next_id_n_block(bitstream , bitstream_size , pos , id_pos);
352 				if (block.size() == 0) {
353 					return false;
354 				}
355 			}
356 			if (block[ 0 ] == ID_AM && block.size() >= 5) {
357 				track = block[ 1 ];
358 				sector = block[ 3 ];
359 				break;
360 			} else {
361 				block.clear();
362 			}
363 		}
364 		// Then for DATA block
365 		int data_pos = 0;
366 		block = get_next_id_n_block(bitstream , bitstream_size , pos , data_pos);
367 		if (block.size() == 0) {
368 			return false;
369 		}
370 		if (block[ 0 ] == DATA_AM && block.size() >= (SECTOR_SIZE + 1) && abs((data_pos - id_pos) - ID_DATA_OFFSET) <= 64) {
371 			break;
372 		}
373 	}
374 
375 	memcpy(sector_data , block.data() + 1 , SECTOR_SIZE);
376 
377 	return true;
378 }
379 
380 const floppy_format_type FLOPPY_IMG_FORMAT = &floppy_image_format_creator<img_format>;
381