1 // license:BSD-3-Clause
2 // copyright-holders: Ansgar Kückes, F. Ulivi
3 /*********************************************************************
4 
5     hpi_dsk.cpp
6 
7     HP9885/HP9895A "HPI" disk images
8 
9     CHS = 67/1/30 (SSDD)
10     CHS = 77/2/30 (DSDD)
11     Sector size 256 bytes
12     Cell size 2 µs
13     Gap1 = 16 x 0x00
14     Gap2 = 34 x 0x00
15     Gap3 = ~490 x 0x00 (depends on actual rotation speed)
16     Sync = 4 x 0x00 + 4 x 0xff
17     ID AM = 0x0e clock + 0x70 data
18     Data AM = 0x0e clock + 0x50 data
19     DefectTrack AM = 0x0e clock + 0xf0 data
20     CRC-16 excludes address markers
21     MMFM/M2FM encoding (LS bit first)
22 
23     The order of sectors in a track depends on the interleave factor
24     which is the distance (in number of sectors) between two consecutively
25     numbered sectors. Interleave factor is specified at formatting time.
26     The default factor is 7. The order of sectors for this factor is:
27     0, 13, 26, 9, 22, 5, 18, 1, 14, 27, 10, 23, 6, 19, 2,
28     15, 28, 11, 24, 7, 20, 3, 16, 29, 12, 25, 8, 21, 4, 17
29 
30     <Track> := [Index hole]|Sector0|Gap2|Sector1|Gap2|...|Sector29|Gap3|
31 
32     <Sector> := ID field|Gap1|Data field
33 
34     <ID field> := Sync|ID AM|Track no.|Sector no.|CRC-16|0x00
35 
36     <Data field> := Sync|Data AM|Data|CRC-16|0x00
37 
38     This format is just a raw image of every sector on a HP-formatted
39     8" floppy disk. Files with this format have no header/trailer and
40     are exactly 1182720 bytes in size (30 sectors, 2 heads, 77 tracks,
41     256 bytes per sector). There's also a "reduced" version holding
42     just 75 cylinders.
43     When loading, the disk image is translated to MMFM encoding so
44     that it can be loaded into HP9885/HP9895 emulator.
45 
46 *********************************************************************/
47 
48 #include "hpi_dsk.h"
49 
50 #include "coretmpl.h" // BIT
51 
52 
53 // Debugging
54 #define VERBOSE 0
55 #define LOG(...)  do { if (VERBOSE) osd_printf_info(__VA_ARGS__); } while (false)
56 
57 constexpr unsigned IL_OFFSET    = 0x12; // Position of interleave factor in HPI image (2 bytes, big-endian)
58 constexpr unsigned DEFAULT_IL   = 7;    // Default interleaving factor
59 constexpr unsigned CELL_SIZE    = 1200; // Bit cell size (1 µs)
60 constexpr uint8_t  ID_AM        = 0x70; // ID address mark
61 constexpr uint8_t  DATA_AM      = 0x50; // Data address mark
62 constexpr uint8_t  AM_CLOCK     = 0x0e; // Clock pattern of AM
63 constexpr uint32_t ID_CD_PATTERN= 0x55552a54;   // C/D pattern of 0xff + ID_AM
64 constexpr uint32_t DATA_CD_PATTERN = 0x55552a44;    // C/D pattern of 0xff + DATA_AM
65 constexpr unsigned GAP1_SIZE    = 17;   // Size of gap 1 (+1)
66 constexpr unsigned GAP2_SIZE    = 35;   // Size of gap 2 (+1)
67 constexpr int ID_DATA_OFFSET = 30 * 16; // Nominal distance (in cells) between ID & DATA AM
68 // Size of image file (holding 77 cylinders)
69 constexpr unsigned HPI_IMAGE_SIZE = HPI_TRACKS * HPI_HEADS * HPI_SECTORS * HPI_SECTOR_SIZE;
70 constexpr unsigned HPI_RED_TRACKS = 75; // Reduced number of tracks
71 constexpr unsigned HPI_9885_TRACKS = 67;    // Tracks visible to HP9885 drives
72 // Size of reduced image file (holding 75 cylinders)
73 constexpr unsigned HPI_RED_IMAGE_SIZE = HPI_RED_TRACKS * HPI_HEADS * HPI_SECTORS * HPI_SECTOR_SIZE;
74 
hpi_format()75 hpi_format::hpi_format()
76 {
77 	(void)HPI_IMAGE_SIZE;
78 	(void)HPI_RED_IMAGE_SIZE;
79 }
80 
identify(io_generic * io,uint32_t form_factor)81 int hpi_format::identify(io_generic *io, uint32_t form_factor)
82 {
83 	uint64_t size = io_generic_size(io);
84 
85 	// we try to stay back and give only 50 points, since another image
86 	// format may also have images of the same size (there is no header and no
87 	// magic number for HPI format...
88 	unsigned dummy_heads;
89 	unsigned dummy_tracks;
90 	if (((form_factor == floppy_image::FF_8) || (form_factor == floppy_image::FF_UNKNOWN)) &&
91 		geometry_from_size(size , dummy_heads , dummy_tracks)) {
92 		return 50;
93 	} else {
94 		return 0;
95 	}
96 }
97 
geometry_from_size(uint64_t image_size,unsigned & heads,unsigned & tracks)98 bool hpi_format::geometry_from_size(uint64_t image_size , unsigned& heads , unsigned& tracks)
99 {
100 	if ((image_size % HPI_SECTOR_SIZE) != 0) {
101 		// Not a whole number of sectors
102 		return false;
103 	}
104 	unsigned sectors = unsigned(image_size / HPI_SECTOR_SIZE);
105 	if ((sectors % HPI_SECTORS) != 0) {
106 		// Not a whole number of tracks
107 		return false;
108 	}
109 	unsigned tot_tracks = sectors / HPI_SECTORS;
110 	// Possible combinations
111 	//
112 	// | Tot tracks | Heads | Tracks | Format |
113 	// |------------+-------+--------+--------|
114 	// | 67         | 1     | 67     | SSDD   |
115 	// | 77         | 1     | 77     | SSDD   |
116 	// | 150        | 2     | 75     | DSDD   |
117 	// | 154        | 2     | 77     | DSDD   |
118 	//
119 	switch (tot_tracks) {
120 	case HPI_9885_TRACKS:
121 		heads = 1;
122 		tracks = HPI_9885_TRACKS;
123 		return true;
124 
125 	case HPI_TRACKS:
126 		heads = 1;
127 		tracks = HPI_TRACKS;
128 		return true;
129 
130 	case HPI_RED_TRACKS * 2:
131 		heads = 2;
132 		tracks = HPI_RED_TRACKS;
133 		return true;
134 
135 	case HPI_TRACKS * 2:
136 		heads = 2;
137 		tracks = HPI_TRACKS;
138 		return true;
139 
140 	default:
141 		return false;
142 	}
143 }
144 
load(io_generic * io,uint32_t form_factor,floppy_image * image)145 bool hpi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
146 {
147 	unsigned heads;
148 	unsigned cylinders;
149 
150 	uint64_t size = io_generic_size(io);
151 	if (!geometry_from_size(size, heads, cylinders)) {
152 		return false;
153 	}
154 	int max_tracks;
155 	int max_heads;
156 	image->get_maximal_geometry(max_tracks , max_heads);
157 	if (cylinders > max_tracks || heads > max_heads) {
158 		return false;
159 	}
160 	image->set_variant(heads == 2 ? floppy_image::DSDD : floppy_image::SSDD);
161 
162 	// Suck in the whole image
163 	std::vector<uint8_t> image_data(size);
164 	io_generic_read(io, (void *)image_data.data(), 0, size);
165 
166 	// Get interleave factor from image
167 	unsigned il = (unsigned)image_data[ IL_OFFSET ] * 256 + image_data[ IL_OFFSET + 1 ];
168 	LOG("I/L from image: %u\n" , il);
169 	if (il < 1 || il >= HPI_SECTORS) {
170 		il = DEFAULT_IL;
171 	}
172 	LOG("Actual I/L: %u\n" , il);
173 
174 	sector_list_t sector_list;
175 	interleaved_sectors(il, sector_list);
176 
177 	unsigned list_offset = 0;
178 	for (unsigned cyl = 0; cyl < cylinders; cyl++) {
179 		for (unsigned head = 0; head < heads; head++) {
180 			std::vector<uint32_t> track_data;
181 			for (unsigned sector = 0; sector < HPI_SECTORS; sector++) {
182 				unsigned real_sector = sector_list[ (sector + list_offset) % HPI_SECTORS ];
183 				unsigned offset_in_image = chs_to_lba(cyl, head, real_sector, heads) * HPI_SECTOR_SIZE;
184 				write_sector(track_data , cyl , real_sector + (head << 7) , &image_data[ offset_in_image ]);
185 			}
186 			fill_with_gap3(track_data);
187 			generate_track_from_levels(cyl , head , track_data , 0 , image);
188 			list_offset = (list_offset + m_track_skew[ il - 1 ][ head ]) % HPI_SECTORS;
189 		}
190 	}
191 	return true;
192 }
193 
save(io_generic * io,floppy_image * image)194 bool hpi_format::save(io_generic *io, floppy_image *image)
195 {
196 	int tracks;
197 	int heads;
198 	image->get_actual_geometry(tracks, heads);
199 
200 	for (int cyl = 0; cyl < tracks; cyl++) {
201 		for (int head = 0; head < heads; head++) {
202 			uint8_t bitstream[ 21000 ];
203 			int bitstream_size;
204 			generate_bitstream_from_track(cyl , head , CELL_SIZE , bitstream , bitstream_size , image , 0);
205 			int pos = 0;
206 			unsigned track_no , head_no , sector_no;
207 			uint8_t sector_data[ HPI_SECTOR_SIZE ];
208 			while (get_next_sector(bitstream , bitstream_size , pos , track_no , head_no , sector_no , sector_data)) {
209 				if (track_no == cyl && head_no == head && sector_no < HPI_SECTORS) {
210 					unsigned offset_in_image = chs_to_lba(cyl, head, sector_no, heads) * HPI_SECTOR_SIZE;
211 					io_generic_write(io, sector_data, offset_in_image, HPI_SECTOR_SIZE);
212 				}
213 			}
214 		}
215 	}
216 	return true;
217 }
218 
name() const219 const char *hpi_format::name() const
220 {
221 	return "hpi";
222 }
223 
description() const224 const char *hpi_format::description() const
225 {
226 	return "HP9895A floppy disk image";
227 }
228 
extensions() const229 const char *hpi_format::extensions() const
230 {
231 	return "hpi";
232 }
233 
supports_save() const234 bool hpi_format::supports_save() const
235 {
236 	return true;
237 }
238 
interleaved_sectors(unsigned il_factor,sector_list_t & sector_list)239 void hpi_format::interleaved_sectors(unsigned il_factor , sector_list_t& sector_list)
240 {
241 	sector_list.fill(0xff);
242 
243 	unsigned idx = HPI_SECTORS - il_factor;
244 	for (unsigned sect = 0; sect < HPI_SECTORS; sect++) {
245 		idx = (idx + il_factor) % HPI_SECTORS;
246 		while (sector_list[ idx ] != 0xff) {
247 			idx = (idx + 1) % HPI_SECTORS;
248 		}
249 		LOG("[%u]=%u\n" , idx , sect);
250 		sector_list[ idx ] = sect;
251 	}
252 }
253 
write_mmfm_bit(std::vector<uint32_t> & buffer,bool data_bit,bool clock_bit)254 void hpi_format::write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit)
255 {
256 	bool had_transition = buffer.size() < 2 ? false : bit_r(buffer, buffer.size() - 1) || bit_r(buffer , buffer.size() - 2);
257 	clock_bit = !data_bit && (clock_bit || !had_transition);
258 	bit_w(buffer , clock_bit , CELL_SIZE);
259 	bit_w(buffer , data_bit , CELL_SIZE);
260 }
261 
write_mmfm_byte(std::vector<uint32_t> & buffer,uint8_t data,uint8_t clock)262 void hpi_format::write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint8_t clock)
263 {
264 	for (unsigned i = 0; i < 8; i++) {
265 		write_mmfm_bit(buffer , util::BIT(data , i) , util::BIT(clock , i));
266 	}
267 }
268 
write_sync(std::vector<uint32_t> & buffer)269 void hpi_format::write_sync(std::vector<uint32_t> &buffer)
270 {
271 	// Sync
272 	// 4x 00
273 	for (unsigned i = 0; i < 4; i++) {
274 		write_mmfm_byte(buffer , 0);
275 	}
276 	// 4x ff
277 	for (unsigned i = 0; i < 4; i++) {
278 		write_mmfm_byte(buffer , 0xff);
279 	}
280 }
281 
write_crc(std::vector<uint32_t> & buffer,uint16_t crc)282 void hpi_format::write_crc(std::vector<uint32_t> &buffer , uint16_t crc)
283 {
284 	// Note that CRC is stored with MSB (x^15) first
285 	for (unsigned i = 0; i < 16; i++) {
286 		write_mmfm_bit(buffer , util::BIT(crc , 15 - i) , 0);
287 	}
288 }
289 
write_sector(std::vector<uint32_t> & buffer,uint8_t track_no,uint8_t sect_head_no,const uint8_t * sect_data)290 void hpi_format::write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_head_no , const uint8_t *sect_data)
291 {
292 	// **** On-disk format of a sector ****
293 	//
294 	// | Offset | Size | Value | Content              |
295 	// |--------+------+-------+----------------------|
296 	// |      0 |    4 |    00 | Sync                 |
297 	// |      4 |    4 |    ff | Sync                 |
298 	// |      8 |    1 |    70 | ID AM (clock = 0e)   |
299 	// |      9 |    1 |    xx | Track no.            |
300 	// |     10 |    1 |    xx | Sector and head no.  |
301 	// |     11 |    2 |    xx | ID CRC               |
302 	// |     13 |    1 |    00 | ID tail              |
303 	// |     14 |   16 |    00 | Gap 1                |
304 	// |     30 |    4 |    00 | Sync                 |
305 	// |     34 |    4 |    ff | Sync                 |
306 	// |     38 |    1 |    50 | Data AM (clock = 0e) |
307 	// |     39 |  256 |    xx | Sector data          |
308 	// |    295 |    2 |    xx | Data CRC             |
309 	// |    297 |    1 |    00 | Data tail            |
310 	// |    298 |   34 |    00 | Gap 2                |
311 	// |    332 |      |       |                      |
312 
313 	// Sync
314 	write_sync(buffer);
315 	// ID AM
316 	write_mmfm_byte(buffer , ID_AM , AM_CLOCK);
317 	auto crc_start = buffer.size();
318 	// Track #
319 	write_mmfm_byte(buffer , track_no);
320 	// Sector/head #
321 	write_mmfm_byte(buffer , sect_head_no);
322 	uint16_t crc = calc_crc_ccitt(buffer , crc_start , buffer.size());
323 	// ID CRC
324 	write_crc(buffer , crc);
325 	// Gap 1
326 	for (unsigned i = 0; i < GAP1_SIZE; i++) {
327 		write_mmfm_byte(buffer , 0);
328 	}
329 	// Sync
330 	write_sync(buffer);
331 	// Data AM
332 	write_mmfm_byte(buffer , DATA_AM , AM_CLOCK);
333 	crc_start = buffer.size();
334 	for (unsigned i = 0; i < HPI_SECTOR_SIZE; i += 2) {
335 		// Data: bytes are swapped in pairs
336 		write_mmfm_byte(buffer , sect_data[ i + 1 ]);
337 		write_mmfm_byte(buffer , sect_data[ i ]);
338 	}
339 	crc = calc_crc_ccitt(buffer , crc_start , buffer.size());
340 	// Data CRC
341 	write_crc(buffer , crc);
342 	// Gap 2
343 	for (unsigned i = 0; i < GAP2_SIZE; i++) {
344 		write_mmfm_byte(buffer , 0);
345 	}
346 }
347 
fill_with_gap3(std::vector<uint32_t> & buffer)348 void hpi_format::fill_with_gap3(std::vector<uint32_t> &buffer)
349 {
350 	// Cell count in a track (1 µs cells in a 1/6 s track)
351 	unsigned cell_count = (500000 * 120) / 360;
352 	unsigned cells_in_buffer = buffer.size();
353 	// Size of gap 3
354 	unsigned gap_3 = (cell_count - cells_in_buffer) / 16;
355 	// Gap 3
356 	for (unsigned i = 0; i < gap_3; i++) {
357 		write_mmfm_byte(buffer , 0);
358 	}
359 	// Last cell to round everything up to 2E+8
360 	if (buffer.size() * CELL_SIZE < 200000000) {
361 		bit_w(buffer , false , 200000000 - buffer.size() * CELL_SIZE);
362 	}
363 }
364 
chs_to_lba(unsigned cylinder,unsigned head,unsigned sector,unsigned heads)365 unsigned hpi_format::chs_to_lba(unsigned cylinder , unsigned head , unsigned sector , unsigned heads)
366 {
367 	return sector + (head + cylinder * heads) * HPI_SECTORS;
368 }
369 
get_next_id_n_block(const uint8_t * bitstream,int bitstream_size,int & pos,int & start_pos)370 std::vector<uint8_t> hpi_format::get_next_id_n_block(const uint8_t *bitstream , int bitstream_size , int& pos , int& start_pos)
371 {
372 	std::vector<uint8_t> res;
373 	uint32_t sr = 0;
374 	// Look for either sync + ID AM or sync + DATA AM
375 	while (pos < bitstream_size && sr != ID_CD_PATTERN && sr != DATA_CD_PATTERN) {
376 		bool bit = util::BIT(bitstream[ pos >> 3 ] , 7 - (pos & 7));
377 		pos++;
378 		sr = (sr << 1) | bit;
379 	}
380 	if (pos == bitstream_size) {
381 		// End of track reached
382 		return res;
383 	}
384 	start_pos = pos;
385 	// ID blocks: Track no. + sector/head no. + CRC
386 	// Data blocks: Sector data + CRC
387 	unsigned to_dump;
388 	if (sr == ID_CD_PATTERN) {
389 		to_dump = 4;
390 		res.push_back(ID_AM);
391 	} else {
392 		to_dump = HPI_SECTOR_SIZE + 2;
393 		res.push_back(DATA_AM);
394 	}
395 	// Align to data cells
396 	pos++;
397 	for (unsigned i = 0; i < to_dump && pos < bitstream_size; i++) {
398 		uint8_t byte = 0;
399 		unsigned j;
400 		for (j = 0; j < 8 && pos < bitstream_size; j++) {
401 			bool bit = util::BIT(bitstream[ pos >> 3 ] , 7 - (pos & 7));
402 			pos += 2;
403 			byte >>= 1;
404 			if (bit) {
405 				byte |= 0x80;
406 			}
407 		}
408 		if (j == 8) {
409 			res.push_back(byte);
410 		}
411 	}
412 	return res;
413 }
414 
get_next_sector(const uint8_t * bitstream,int bitstream_size,int & pos,unsigned & track,unsigned & head,unsigned & sector,uint8_t * sector_data)415 bool hpi_format::get_next_sector(const uint8_t *bitstream , int bitstream_size , int& pos , unsigned& track , unsigned& head , unsigned& sector , uint8_t *sector_data)
416 {
417 	std::vector<uint8_t> block;
418 	while (true) {
419 		// Scan for ID block first
420 		int id_pos = 0;
421 		while (true) {
422 			if (block.size() == 0) {
423 				block = get_next_id_n_block(bitstream , bitstream_size , pos , id_pos);
424 				if (block.size() == 0) {
425 					return false;
426 				}
427 			}
428 			if (block[ 0 ] == ID_AM && block.size() >= 3) {
429 				track = block[ 1 ];
430 				head = block[ 2 ] >> 7;
431 				sector = block[ 2 ] & 0x7f;
432 				break;
433 			} else {
434 				block.clear();
435 			}
436 		}
437 		// Then for DATA block
438 		int data_pos = 0;
439 		block = get_next_id_n_block(bitstream , bitstream_size , pos , data_pos);
440 		if (block.size() == 0) {
441 			return false;
442 		}
443 		if (block[ 0 ] == DATA_AM && block.size() >= (HPI_SECTOR_SIZE + 1) && abs((data_pos - id_pos) - ID_DATA_OFFSET) <= 16) {
444 			break;
445 		}
446 	}
447 
448 	for (unsigned i = 0; i < HPI_SECTOR_SIZE; i += 2) {
449 		sector_data[ i ] = block[ i + 2 ];
450 		sector_data[ i + 1 ] = block[ i + 1 ];
451 	}
452 
453 	return true;
454 }
455 
456 // This table comes straight from hp9895 firmware (it's @ 0x0f90)
457 // For each interleave factor in [1..29] it stores the number of positions
458 // to move forward in the interleaved sector list when beginning a new track.
459 // There are different offsets for tracks on head 0 and tracks on head 1.
460 const uint8_t hpi_format::m_track_skew[ HPI_SECTORS - 1 ][ HPI_HEADS ] = {
461 	{ 0x1c , 0x18 },    // Interleave = 1
462 	{ 0x1c , 0x18 },    // Interleave = 2
463 	{ 0x1c , 0x18 },    // Interleave = 3
464 	{ 0x1d , 0x1a },    // Interleave = 4
465 	{ 0x1a , 0x18 },    // Interleave = 5
466 	{ 0x19 , 0x18 },    // Interleave = 6
467 	{ 0x00 , 0x00 },    // Interleave = 7
468 	{ 0x1d , 0x1d },    // Interleave = 8
469 	{ 0x1c , 0x1c },    // Interleave = 9
470 	{ 0x15 , 0x15 },    // Interleave = 10
471 	{ 0x00 , 0x00 },    // Interleave = 11
472 	{ 0x19 , 0x19 },    // Interleave = 12
473 	{ 0x00 , 0x00 },    // Interleave = 13
474 	{ 0x1d , 0x1d },    // Interleave = 14
475 	{ 0x10 , 0x10 },    // Interleave = 15
476 	{ 0x1d , 0x1d },    // Interleave = 16
477 	{ 0x00 , 0x00 },    // Interleave = 17
478 	{ 0x19 , 0x19 },    // Interleave = 18
479 	{ 0x00 , 0x00 },    // Interleave = 19
480 	{ 0x15 , 0x15 },    // Interleave = 20
481 	{ 0x1c , 0x1c },    // Interleave = 21
482 	{ 0x1d , 0x1d },    // Interleave = 22
483 	{ 0x00 , 0x00 },    // Interleave = 23
484 	{ 0x19 , 0x19 },    // Interleave = 24
485 	{ 0x1a , 0x1a },    // Interleave = 25
486 	{ 0x1d , 0x1d },    // Interleave = 26
487 	{ 0x1c , 0x1c },    // Interleave = 27
488 	{ 0x1d , 0x1d },    // Interleave = 28
489 	{ 0x00 , 0x00 }     // Interleave = 29
490 };
491 
492 const floppy_format_type FLOPPY_HPI_FORMAT = &floppy_image_format_creator<hpi_format>;
493