1 // license:LGPL-2.1+
2 // copyright-holders:Michael Zapf
3 /*************************************************************************
4 
5     Hard disk emulation: Format implementation
6     ------------------------------------------
7 
8     This is the format implementation for MFM hard disks, similar to the
9     modular format concept of floppy drives in MAME/MESS.
10 
11     The base class is mfmhd_image_format_t; it contains some methods for
12     encoding and decoding MFM. Although MFM hard disks should also be able to
13     manage FM recording, we do not plan for FM recording here.
14 
15     The encode/decode methods rely on a parameter "encoding";
16     see imagedev/mfmhd.c for a discussion. Essentially, it determines whether
17     data are read bitwise or bytewise, and whether clock bits are separated
18     or interleaved.
19 
20     The base class is abstract; you must create a subclass to use it. This
21     file delivers one subclass called mfmhd_generic_format.
22 
23     In order to use this format, you must pass the creator identifier to the
24     macro MCFG_MFM_HARDDISK_CONN_ADD. See emu/bus/ti99_peb/hfdc.c for an
25     example.
26 
27 
28     Generic MFM format
29     ------------------
30     The heart of this class are the methods load and save. They are designed
31     to read sector data from a CHD file and reconstruct the track image (load),
32     or to take a track image, isolate the sector data, and store them
33     into the CHD (save).
34 
35     Rebuilding the track image means to create sector headers, allocate gaps,
36     add sync areas, and CRC values. Also, the sectors must be arranged
37     according to the "interleave" parameter and the "skew" parameters for
38     heads and cylinders. While the skews are commonly set to 0, the interleave
39     is often used to fine-tune the transfer speed between the drive hardware
40     and the host system.
41 
42     Also, the format allows for two header setups.
43     a) PC-AT-compatible header: four bytes long (ident, cylinder, head, sector);
44        the sector size is always 512 bytes.
45     b) Custom headers: five bytes long (..., sector size). The custom headers
46        are used in non-PC systems.
47 
48     ECC: While floppy drives make use of a CRC field to check the data integrity,
49     hard disks use an ECC (error correcting code). The ECC length is 4 bytes
50     or longer, depending on the desired correction capability. The ECC length
51     can also be specified for this format.
52 
53     However, for this version, we do not support ECC computation, but instead
54     we use CRC. This is indicated by setting the "ECC length" parameter to -1.
55 
56     Format autodetect
57     -----------------
58     While formatting a hard disk, format parameters are likely to change, so
59     we have to find out about the new layout and store the metadata into the
60     CHD if they were modified.
61 
62     This is done in the save method. This method does not only retrieve the
63     sector contents but also counts the gap bytes and sync bytes so that
64     they can be stored in the CHD.
65 
66     - Interleave detection: save counts the number of sectors between sector
67       number n and sector number n+1.
68 
69     - Skew detection: Skew is determined by three tracks: (cyl,head)=
70       (0,0), (1,0), and (0,1). For this purpose we use the m_secnumber list.
71 
72     - Header length is detemined by the first sector on (0,0). This is done
73       by checking the header against the following two CRC bytes. If they
74       match for 4 bytes, we have an AT-style header, else a custom header.
75 
76     - Gap and sync lengths are determined by the first track (0,0). They are
77       actually not expected to change, unless they are undefined before first
78       use, or the controller or its driver changes. We assume that track
79       (0,0) is actually rewritten during reformatting.
80 
81     Since write precompensation and reduced write current cannot be seen
82     on the track image directly, those two values have to be set by the
83     hard disk device itseltf.
84 
85     Inhibit autodetect
86     ------------------
87     In case we do not want the format to detect the layout but want to ensure
88     an immutable format, the save_param method may be overwritten to return
89     false for all or a particular group of parameters. The generic format
90     offers a save_param method which always returns true.
91 
92     The effect of inhibiting the autodetection is that the layout parameters
93     as found on the CHD are used if available; otherwise defaults are used.
94 
95     Defaults
96     --------
97     The generic format defines a method get_default which returns safe values
98     for layout parameters. It can be overwritten for specific formats.
99 
100     Debugging
101     ---------
102     There is a set of debug flags (starting with TRACE_) that can be set to 1;
103     after recompiling you will get additional output. Since this class is not
104     a descendant of device_t we do not have a tag for output; for a better
105     overview in the logfile the hard disk device passes its tag to the base
106     class.
107 
108 
109     TODO
110     ----
111     - Add ECC computation
112 
113 
114     Michael Zapf
115     August 2015
116 
117 **************************************************************************/
118 
119 #include "mfm_hd.h"
120 #include "imageutl.h"
121 
122 #define TRACE_RWTRACK 0
123 #define TRACE_LAYOUT 0
124 #define TRACE_IMAGE 0
125 #define TRACE_DETAIL 0
126 #define TRACE_FORMAT 0
127 
128 /*
129     Accept the new layout parameters and reset the sector number fields
130     used for skew calculation.
131 */
set_layout_params(mfmhd_layout_params param)132 void mfmhd_image_format_t::set_layout_params(mfmhd_layout_params param)
133 {
134 	m_param = m_param_old = param;
135 	m_secnumber[0] = m_secnumber[1] = m_secnumber[2] = -1;
136 }
137 
138 /*
139     Encode some value with data-type clock bits.
140 */
mfm_encode(uint16_t * trackimage,int & position,uint8_t byte,int count)141 void mfmhd_image_format_t::mfm_encode(uint16_t* trackimage, int& position, uint8_t byte, int count)
142 {
143 	mfm_encode_mask(trackimage, position, byte, count, 0x00);
144 }
145 
146 /*
147     Encode an A1 value with mark-type clock bits.
148 */
mfm_encode_a1(uint16_t * trackimage,int & position)149 void mfmhd_image_format_t::mfm_encode_a1(uint16_t* trackimage, int& position)
150 {
151 	m_current_crc = 0xffff;
152 	mfm_encode_mask(trackimage, position, 0xa1, 1, 0x04);
153 }
154 
155 /*
156     Encode a byte value with a given clock bit mask. Used by both mfm_encode
157     and mfm_encode_a1 methods.
158 */
mfm_encode_mask(uint16_t * trackimage,int & position,uint8_t byte,int count,int mask)159 void mfmhd_image_format_t::mfm_encode_mask(uint16_t* trackimage, int& position, uint8_t byte, int count, int mask)
160 {
161 	uint16_t encclock = 0;
162 	uint16_t encdata = 0;
163 	uint8_t thisbyte = byte;
164 	bool mark = (mask != 0x00);
165 
166 	m_current_crc = ccitt_crc16_one(m_current_crc, byte);
167 
168 	for (int i=0; i < 8; i++)
169 	{
170 		encdata <<= 1;
171 		encclock <<= 1;
172 
173 		if (m_param.encoding == MFM_BITS || m_param.encoding == MFM_BYTE)
174 		{
175 			// skip one position for later interleaving
176 			encdata <<= 1;
177 			encclock <<= 1;
178 		}
179 
180 		if (thisbyte & 0x80)
181 		{
182 			// Encoding 1 => 01
183 			encdata |= 1;
184 			m_lastbit = true;
185 		}
186 		else
187 		{
188 			// Encoding 0 => x0
189 			// If the bit in the mask is set, suppress the clock bit
190 			// Also, if we use the simplified encoding, don't set the clock bits
191 			if (m_lastbit == false && m_param.encoding != SEPARATED_SIMPLE && (mask & 0x80) == 0) encclock |= 1;
192 			m_lastbit = false;
193 		}
194 		mask <<= 1;
195 		// For simplified encoding, set all clock bits to indicate a mark
196 		if (m_param.encoding == SEPARATED_SIMPLE && mark) encclock |= 1;
197 		thisbyte <<= 1;
198 	}
199 
200 	if (m_param.encoding == MFM_BITS || m_param.encoding == MFM_BYTE)
201 		encclock <<= 1;
202 	else
203 		encclock <<= 8;
204 
205 	trackimage[position++] = (encclock | encdata);
206 
207 	// When we write the byte multiple times, check whether the next encoding
208 	// differs from the previous because of the last bit
209 
210 	if (m_param.encoding == MFM_BITS || m_param.encoding == MFM_BYTE)
211 	{
212 		encclock &= 0x7fff;
213 		if ((byte & 0x80)==0 && m_lastbit==false) encclock |= 0x8000;
214 	}
215 
216 	for (int j=1; j < count; j++)
217 	{
218 		trackimage[position++] = (encclock | encdata);
219 		m_current_crc = ccitt_crc16_one(m_current_crc, byte);
220 	}
221 }
222 
223 /*
224     Decode an MFM cell pattern into a byte value.
225     Clock bits and data bits are assumed to be interleaved (cdcdcdcdcdcdcdcd);
226     the 8 data bits are returned.
227 */
mfm_decode(uint16_t raw)228 uint8_t mfmhd_image_format_t::mfm_decode(uint16_t raw)
229 {
230 	unsigned int value = 0;
231 
232 	for (int i=0; i < 8; i++)
233 	{
234 		value <<= 1;
235 
236 		value |= (raw & 0x4000);
237 		raw <<= 2;
238 	}
239 	return (value >> 14) & 0xff;
240 }
241 
242 /*
243     For debugging. Outputs the byte array in a xxd-like way.
244 */
showtrack(uint16_t * enctrack,int length)245 void mfmhd_image_format_t::showtrack(uint16_t* enctrack, int length)
246 {
247 	for (int i=0; i < length; i+=16)
248 	{
249 		osd_printf_verbose("%07x: ", i);
250 		for (int j=0; j < 16; j++)
251 		{
252 			osd_printf_verbose("%04x ", enctrack[i+j]);
253 		}
254 		osd_printf_verbose(" ");
255 		osd_printf_verbose("\n");
256 	}
257 }
258 
259 // ======================================================================
260 //    Generic MFM HD format
261 // ======================================================================
262 
263 const mfmhd_format_type MFMHD_GEN_FORMAT = &mfmhd_image_format_creator<mfmhd_generic_format>;
264 
265 /*
266     Calculate the ident byte from the cylinder. The specification does not
267     define idents beyond cylinder 1023, but formatting programs seem to
268     continue with 0xfd for cylinders between 1024 and 2047.
269 */
cylinder_to_ident(int cylinder)270 uint8_t mfmhd_generic_format::cylinder_to_ident(int cylinder)
271 {
272 	if (cylinder < 256) return 0xfe;
273 	if (cylinder < 512) return 0xff;
274 	if (cylinder < 768) return 0xfc;
275 	return 0xfd;
276 }
277 
278 /*
279     Returns the linear sector number, given the CHS data.
280 
281       C,H,S
282     | 0,0,0 | 0,0,1 | 0,0,2 | ...
283     | 0,1,0 | 0,1,1 | 0,1,2 | ...
284     ...
285     | 1,0,0 | ...
286     ...
287 */
chs_to_lba(int cylinder,int head,int sector)288 int mfmhd_generic_format::chs_to_lba(int cylinder, int head, int sector)
289 {
290 	if ((cylinder < m_param.cylinders) && (head < m_param.heads) && (sector < m_param.sectors_per_track))
291 	{
292 		return (cylinder * m_param.heads + head) * m_param.sectors_per_track + sector;
293 	}
294 	else return -1;
295 }
296 
load(chd_file * chdfile,uint16_t * trackimage,int tracksize,int cylinder,int head)297 chd_error mfmhd_generic_format::load(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head)
298 {
299 	chd_error state = CHDERR_NONE;
300 	uint8_t sector_content[16384];
301 
302 	int sectorcount = m_param.sectors_per_track;
303 	int size = m_param.sector_size;
304 	int position = 0; // will be incremented by each encode call
305 	int sec_number = 0;
306 	int identfield = 0;
307 	int cylfield = 0;
308 	int headfield = 0;
309 	int sizefield = (size >> 7)-1;
310 
311 	// If we don't have interleave data in the CHD, take a default
312 	if (m_param.interleave==0)
313 	{
314 		m_param.interleave = get_default(MFMHD_IL);
315 		m_param.cylskew = get_default(MFMHD_CSKEW);
316 		m_param.headskew = get_default(MFMHD_HSKEW);
317 	}
318 
319 	int sec_il_start = (m_param.cylskew * cylinder + m_param.headskew * head) % sectorcount;
320 	int delta = (sectorcount + m_param.interleave-1) / m_param.interleave;
321 
322 	if (TRACE_RWTRACK) osd_printf_verbose("%s: Load track (c=%d,h=%d) from CHD, interleave=%d, cylskew=%d, headskew=%d\n", tag(), cylinder, head, m_param.interleave, m_param.cylskew, m_param.headskew);
323 
324 	m_lastbit = false;
325 
326 	if (m_param.sync==0)
327 	{
328 		m_param.gap1 = get_default(MFMHD_GAP1);
329 		m_param.gap2 = get_default(MFMHD_GAP2);
330 		m_param.gap3 = get_default(MFMHD_GAP3);
331 		m_param.sync = get_default(MFMHD_SYNC);
332 		m_param.headerlen = get_default(MFMHD_HLEN);
333 		m_param.ecctype = get_default(MFMHD_ECC);
334 	}
335 
336 	// Gap 1
337 	mfm_encode(trackimage, position, 0x4e, m_param.gap1);
338 
339 	if (TRACE_LAYOUT) osd_printf_verbose("%s: cyl=%d head=%d: sector sequence = ", tag(), cylinder, head);
340 
341 	sec_number = sec_il_start;
342 	for (int sector = 0; sector < sectorcount; sector++)
343 	{
344 		if (TRACE_LAYOUT) osd_printf_verbose("%02d ", sec_number);
345 
346 		// Sync gap
347 		mfm_encode(trackimage, position, 0x00, m_param.sync);
348 
349 		// Write IDAM
350 		mfm_encode_a1(trackimage, position);
351 
352 		// Write header
353 		identfield = cylinder_to_ident(cylinder);
354 		cylfield = cylinder & 0xff;
355 		headfield = head & 0x0f;
356 		if (m_param.headerlen==5)
357 			headfield |= ((cylinder & 0x700)>>4);
358 
359 		mfm_encode(trackimage, position, identfield);
360 		mfm_encode(trackimage, position, cylfield);
361 		mfm_encode(trackimage, position, headfield);
362 		mfm_encode(trackimage, position, sec_number);
363 		if (m_param.headerlen==5)
364 			mfm_encode(trackimage, position, sizefield);
365 
366 		// Write CRC for header.
367 		int crc = m_current_crc;
368 		mfm_encode(trackimage, position, (crc >> 8) & 0xff);
369 		mfm_encode(trackimage, position, crc & 0xff);
370 
371 		// Gap 2
372 		mfm_encode(trackimage, position, 0x4e, m_param.gap2);
373 
374 		// Sync
375 		mfm_encode(trackimage, position, 0x00, m_param.sync);
376 
377 		// Write DAM
378 		mfm_encode_a1(trackimage, position);
379 		mfm_encode(trackimage, position, 0xfb);
380 
381 		// Get sector content from CHD
382 		int lbaposition = chs_to_lba(cylinder, head, sec_number);
383 		if (lbaposition>=0)
384 		{
385 			chd_error state = chdfile->read_units(lbaposition, sector_content);
386 			if (state != CHDERR_NONE) break;
387 		}
388 		else
389 		{
390 			osd_printf_verbose("%s: Invalid CHS data (%d,%d,%d); not loading from CHD\n", tag(), cylinder, head, sector);
391 		}
392 
393 		for (int i=0; i < size; i++)
394 			mfm_encode(trackimage, position, sector_content[i]);
395 
396 		// Write CRC for content.
397 		crc = m_current_crc;
398 		mfm_encode(trackimage, position, (crc >> 8) & 0xff);
399 		mfm_encode(trackimage, position, crc & 0xff);
400 
401 		// Gap 3
402 		mfm_encode(trackimage, position, 0x00, 3);
403 		mfm_encode(trackimage, position, 0x4e, m_param.gap3-3);
404 
405 		// Calculate next sector number
406 		sec_number += delta;
407 		if (sec_number >= sectorcount)
408 		{
409 			sec_il_start = (sec_il_start+1) % delta;
410 			sec_number = sec_il_start;
411 		}
412 	}
413 	if (TRACE_LAYOUT) osd_printf_verbose("\n");
414 
415 	// Gap 4
416 	if (state == CHDERR_NONE)
417 	{
418 		// Fill the rest with 0x4e
419 		mfm_encode(trackimage, position, 0x4e, tracksize-position);
420 		if (TRACE_IMAGE) showtrack(trackimage, tracksize);
421 	}
422 	return state;
423 }
424 
425 /*
426     State names for analyzing the track image.
427 */
428 enum
429 {
430 	SEARCH_A1=0,
431 	FOUND_A1,
432 	DAM_FOUND,
433 	CHECK_CRC
434 };
435 
save(chd_file * chdfile,uint16_t * trackimage,int tracksize,int current_cylinder,int current_head)436 chd_error mfmhd_generic_format::save(chd_file* chdfile, uint16_t* trackimage, int tracksize, int current_cylinder, int current_head)
437 {
438 	if (TRACE_RWTRACK) osd_printf_verbose("%s: write back (c=%d,h=%d) to CHD\n", tag(), current_cylinder, current_head);
439 
440 	uint8_t buffer[16384]; // for header or sector content
441 
442 	int bytepos = 0;
443 	int state = SEARCH_A1;
444 	int count = 0;
445 	int pos = 0;
446 	uint16_t crc = 0;
447 	uint8_t byte;
448 	bool search_header = true;
449 
450 	int ident = 0;
451 	int cylinder = 0;
452 	int head = 0;
453 	int sector = 0;
454 	int size = 0;
455 
456 	int headerpos = 0;
457 
458 	int interleave = 0;
459 	int interleave_prec = -1;
460 	bool check_interleave = true;
461 	bool check_skew = true;
462 
463 	int gap1 = 0;
464 	int ecctype = 0;
465 
466 	// if (current_cylinder==0 && current_head==0) showtrack(trackimage, tracksize);
467 
468 	// If we want to detect gaps, we only do it on cylinder 0, head 0
469 	// This makes it safer to detect the header length
470 	// (There is indeed some chance that we falsely assume a header length of 4
471 	// because the two bytes behind happen to be a valid CRC value)
472 	if (save_param(MFMHD_GAP1) && current_cylinder==0 && current_head==0)
473 	{
474 		m_param.gap1 = 0;
475 		m_param.gap2 = 0;
476 		m_param.gap3 = 0;
477 		m_param.sync = 0;
478 		// 4-byte headers are used for the IBM-AT format
479 		// 5-byte headers are used in other formats
480 		m_param.headerlen = 4;
481 		m_param.ecctype = 0;
482 	}
483 
484 	// AT format implies 512 bytes per sector
485 	int sector_length = 512;
486 
487 	// Only check once
488 	bool countgap1 = (m_param.gap1==0);
489 	bool countgap2 = false;
490 	bool countgap3 = false;
491 	bool countsync = false;
492 
493 	chd_error chdstate = CHDERR_NONE;
494 
495 	if (TRACE_IMAGE)
496 	{
497 		for (int i=0; i < tracksize; i++)
498 		{
499 			if ((i % 16)==0) osd_printf_verbose("\n%04x: ", i);
500 			osd_printf_verbose("%02x ", (m_param.encoding==MFM_BITS || m_param.encoding==MFM_BYTE)? mfm_decode(trackimage[i]) : (trackimage[i]&0xff));
501 		}
502 		osd_printf_verbose("\n");
503 	}
504 
505 	// We have to go through the bytes of the track and save a sector as soon as one shows up
506 
507 	while (bytepos < tracksize)
508 	{
509 		// Decode the next 16 bits
510 		if (m_param.encoding==MFM_BITS || m_param.encoding==MFM_BYTE)
511 		{
512 			byte = mfm_decode(trackimage[bytepos]);
513 		}
514 		else byte = (trackimage[bytepos] & 0xff);
515 
516 		switch (state)
517 		{
518 		case SEARCH_A1:
519 			// Counting gaps and sync
520 			if (countgap2)
521 			{
522 				if (byte == 0x4e) m_param.gap2++;
523 				else if (byte == 0) { countsync = true; countgap2 = false; }
524 			}
525 
526 			if (countsync)
527 			{
528 				if (byte == 0) m_param.sync++;
529 				else countsync = false;
530 			}
531 
532 			if (countgap3)
533 			{
534 				if (byte != 0x00 || m_param.gap3 < 4) m_param.gap3++;
535 				else countgap3 = false;
536 			}
537 
538 			if (((m_param.encoding==MFM_BITS || m_param.encoding==MFM_BYTE) && trackimage[bytepos]==0x4489)
539 				|| (m_param.encoding==SEPARATED && trackimage[bytepos]==0x0aa1)
540 				|| (m_param.encoding==SEPARATED_SIMPLE && trackimage[bytepos]==0xffa1))
541 			{
542 				state = FOUND_A1;
543 				count = (search_header? m_param.headerlen : (sector_length+1)) + 2;
544 				crc = 0x443b; // init value with a1
545 				pos = 0;
546 			}
547 			bytepos++;
548 			break;
549 
550 		case FOUND_A1:
551 			crc = ccitt_crc16_one(crc, byte);
552 			// osd_printf_verbose("%s: MFM HD: Byte = %02x, CRC=%04x\n", tag(), byte, crc);
553 
554 			// Put byte into buffer
555 			// but not the data mark and the CRC
556 			if (search_header || (count > 2 &&  count < sector_length+3)) buffer[pos++] = byte;
557 
558 			// Stop counting gap1
559 			if (search_header && countgap1)
560 			{
561 				gap1 = bytepos-1;
562 				countgap1 = false;
563 			}
564 
565 			if (--count == 0)
566 			{
567 				if (crc==0)
568 				{
569 					if (search_header)
570 					{
571 						// Found a header
572 						ident = buffer[0];
573 						cylinder = buffer[1];
574 						// For non-PC-AT formats, highest three bits are in the head field
575 						if (m_param.headerlen == 5) cylinder |= ((buffer[2]&0x70)<<4);
576 						else
577 						{
578 							osd_printf_verbose("%s: Unexpected header size: %d, cylinder=%d, position=%04x\n", tag(), m_param.headerlen, cylinder, bytepos);
579 							showtrack(trackimage, tracksize);
580 						}
581 
582 						head = buffer[2] & 0x0f;
583 						sector = buffer[3];
584 						int identexp = cylinder_to_ident(cylinder);
585 
586 						if (identexp != ident)
587 						{
588 							osd_printf_verbose("%s: Field error; ident = %02x (expected %02x) for sector chs=(%d,%d,%d)\n", tag(), ident, identexp, cylinder, head, sector);
589 						}
590 
591 						if (cylinder != current_cylinder)
592 						{
593 							osd_printf_verbose("%s: Sector header of sector %d defines cylinder = %02x (should be %02x)\n", tag(), sector, cylinder, current_cylinder);
594 						}
595 
596 						if (head != current_head)
597 						{
598 							osd_printf_verbose("%s: Sector header of sector %d defines head = %02x (should be %02x)\n", tag(), sector, head, current_head);
599 						}
600 
601 						// Check skew
602 						// We compare the beginning of this track with the track on the next head and the track on the next cylinder
603 						if (check_skew && cylinder < 2 && head < 2)
604 						{
605 							m_secnumber[cylinder*2 + head] = sector;
606 							check_skew=false;
607 						}
608 
609 						// Count the sectors for the interleave
610 						if (check_interleave)
611 						{
612 							if (interleave_prec == -1) interleave_prec = sector;
613 							else
614 							{
615 								if (sector == interleave_prec+1) check_interleave = false;
616 								interleave++;
617 							}
618 						}
619 
620 						if (interleave == 0) interleave = sector - buffer[3];
621 
622 						// When we have 4-byte headers, the sector length is 512 bytes
623 						if (m_param.headerlen == 5)
624 						{
625 							size = buffer[4];
626 							sector_length = 128 << (size&0x07);
627 							ecctype = (size&0xf0)>>4;
628 						}
629 
630 						search_header = false;
631 						if (TRACE_DETAIL) osd_printf_verbose("%s: Found sector chs=(%d,%d,%d)\n", tag(), cylinder, head, sector);
632 						headerpos = pos;
633 						// Start the GAP2 counter (if not already determined)
634 						if (m_param.gap2==0) countgap2 = true;
635 					}
636 					else
637 					{
638 						// Sector contents
639 						// Write the sectors to the CHD
640 						int lbaposition = chs_to_lba(cylinder, head, sector);
641 						if (lbaposition>=0)
642 						{
643 							if (TRACE_DETAIL) osd_printf_verbose("%s: Writing sector chs=(%d,%d,%d) to CHD\n", tag(), current_cylinder, current_head, sector);
644 							chdstate = chdfile->write_units(chs_to_lba(current_cylinder, current_head, sector), buffer);
645 
646 							if (chdstate != CHDERR_NONE)
647 							{
648 								osd_printf_verbose("%s: Write error while writing sector chs=(%d,%d,%d)\n", tag(), cylinder, head, sector);
649 							}
650 						}
651 						else
652 						{
653 							osd_printf_verbose("%s: Invalid CHS data in track image: (%d,%d,%d); not saving to CHD\n", tag(), cylinder, head, sector);
654 						}
655 						if (m_param.gap3==0) countgap3 = true;
656 						search_header = true;
657 					}
658 				}
659 				else
660 				{
661 					// Let's test for a 5-byte header
662 					if (search_header && m_param.headerlen==4 && current_cylinder==0 && current_head==0)
663 					{
664 						if (TRACE_DETAIL) osd_printf_verbose("%s: CRC error for 4-byte header; trying 5 bytes\n", tag());
665 						m_param.headerlen=5;
666 						count = 1;
667 						bytepos++;
668 						break;
669 					}
670 					else
671 					{
672 						osd_printf_verbose("%s: CRC error in %s of (%d,%d,%d)\n", tag(), search_header? "header" : "data", cylinder, head, sector);
673 						search_header = true;
674 					}
675 				}
676 				// search next A1
677 				state = SEARCH_A1;
678 
679 				if (!search_header && (pos - headerpos) > 30)
680 				{
681 					osd_printf_verbose("%s: Error; missing DAM; searching next header\n", tag());
682 					search_header = true;
683 				}
684 			}
685 			bytepos++;
686 			break;
687 		}
688 	}
689 
690 	if (check_interleave == false && save_param(MFMHD_IL))
691 	{
692 		// Successfully determined the interleave
693 		m_param.interleave = interleave;
694 		if (TRACE_FORMAT)
695 			if (current_cylinder==0 && current_head==0) osd_printf_verbose("%s: Determined interleave = %d\n", tag(), m_param.interleave);
696 	}
697 
698 	if (check_skew == false)
699 	{
700 		if (m_secnumber[0] != -1)
701 		{
702 			if (m_secnumber[1] != -1)
703 			{
704 				if (save_param(MFMHD_HSKEW)) m_param.headskew = m_secnumber[1]-m_secnumber[0];
705 				if (TRACE_FORMAT) osd_printf_verbose("%s: Determined head skew = %d\n", tag(), m_param.headskew);
706 			}
707 			if (m_secnumber[2] != -1)
708 			{
709 				if (save_param(MFMHD_CSKEW)) m_param.cylskew = m_secnumber[2]-m_secnumber[0];
710 				if (TRACE_FORMAT) osd_printf_verbose("%s: Determined cylinder skew = %d\n", tag(), m_param.cylskew);
711 			}
712 		}
713 	}
714 
715 	gap1 -= m_param.sync;
716 	ecctype = -1;   // lock to CRC until we have a support for ECC
717 
718 	if (current_cylinder==0 && current_head==0)
719 	{
720 		// If we want to detect gaps, store the new value into the param object
721 		// The other gaps have already been written directly to the param object above,
722 		// unless save_param returned false (or we were not on cylinder 0, head 0)
723 		if (save_param(MFMHD_GAP1)) m_param.gap1 = gap1;
724 		if (save_param(MFMHD_ECC)) m_param.ecctype = ecctype;
725 	}
726 	return chdstate;
727 }
728 
729 /*
730     Deliver default values.
731 */
get_default(mfmhd_param_t type)732 int mfmhd_generic_format::get_default(mfmhd_param_t type)
733 {
734 	switch (type)
735 	{
736 	case MFMHD_IL: return 4;
737 	case MFMHD_HSKEW:
738 	case MFMHD_CSKEW: return 0;
739 	case MFMHD_WPCOM:               // Write precompensation cylinder (-1 = none)
740 	case MFMHD_RWC: return -1;      // Reduced write current cylinder (-1 = none)
741 	case MFMHD_GAP1: return 16;
742 	case MFMHD_GAP2: return 3;
743 	case MFMHD_GAP3: return 18;
744 	case MFMHD_SYNC: return 13;
745 	case MFMHD_HLEN: return 5;
746 	case MFMHD_ECC: return -1;      // -1: use CRC instead of ECC
747 	}
748 	return -1;
749 }
750