summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/mfm_hd.cpp
blob: 032be8622cb270dbb27ad33bd9257c0a1c54881a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/*************************************************************************

    Hard disk emulation: Format implementation
    ------------------------------------------

    This is the format implementation for MFM hard disks, similar to the
    modular format concept of floppy drives in MAME/MESS.

    The base class is mfmhd_image_format_t; it contains some methods for
    encoding and decoding MFM. Although MFM hard disks should also be able to
    manage FM recording, we do not plan for FM recording here.

    The encode/decode methods rely on a parameter "encoding";
    see imagedev/mfmhd.c for a discussion. Essentially, it determines whether
    data are read bitwise or bytewise, and whether clock bits are separated
    or interleaved.

    The base class is abstract; you must create a subclass to use it. This
    file delivers one subclass called mfmhd_generic_format.

    In order to use this format, you must pass the creator identifier to the
    macro MCFG_MFM_HARDDISK_CONN_ADD. See emu/bus/ti99_peb/hfdc.c for an
    example.


    Generic MFM format
    ------------------
    The heart of this class are the methods load and save. They are designed
    to read sector data from a CHD file and reconstruct the track image (load),
    or to take a track image, isolate the sector data, and store them
    into the CHD (save).

    Rebuilding the track image means to create sector headers, allocate gaps,
    add sync areas, and CRC values. Also, the sectors must be arranged
    according to the "interleave" parameter and the "skew" parameters for
    heads and cylinders. While the skews are commonly set to 0, the interleave
    is often used to fine-tune the transfer speed between the drive hardware
    and the host system.

    Also, the format allows for two header setups.
    a) PC-AT-compatible header: four bytes long (ident, cylinder, head, sector);
       the sector size is always 512 bytes.
    b) Custom headers: five bytes long (..., sector size). The custom headers
       are used in non-PC systems.

    ECC: While floppy drives make use of a CRC field to check the data integrity,
    hard disks use an ECC (error correcting code). The ECC length is 4 bytes
    or longer, depending on the desired correction capability. The ECC length
    can also be specified for this format.

    However, for this version, we do not support ECC computation, but instead
    we use CRC. This is indicated by setting the "ECC length" parameter to -1.

    Format autodetect
    -----------------
    While formatting a hard disk, format parameters are likely to change, so
    we have to find out about the new layout and store the metadata into the
    CHD if they were modified.

    This is done in the save method. This method does not only retrieve the
    sector contents but also counts the gap bytes and sync bytes so that
    they can be stored in the CHD.

    - Interleave detection: save counts the number of sectors between sector
      number n and sector number n+1.

    - Skew detection: Skew is determined by three tracks: (cyl,head)=
      (0,0), (1,0), and (0,1). For this purpose we use the m_secnumber list.

    - Header length is detemined by the first sector on (0,0). This is done
      by checking the header against the following two CRC bytes. If they
      match for 4 bytes, we have an AT-style header, else a custom header.

    - Gap and sync lengths are determined by the first track (0,0). They are
      actually not expected to change, unless they are undefined before first
      use, or the controller or its driver changes. We assume that track
      (0,0) is actually rewritten during reformatting.

    Since write precompensation and reduced write current cannot be seen
    on the track image directly, those two values have to be set by the
    hard disk device itseltf.

    Inhibit autodetect
    ------------------
    In case we do not want the format to detect the layout but want to ensure
    an immutable format, the save_param method may be overwritten to return
    false for all or a particular group of parameters. The generic format
    offers a save_param method which always returns true.

    The effect of inhibiting the autodetection is that the layout parameters
    as found on the CHD are used if available; otherwise defaults are used.

    Defaults
    --------
    The generic format defines a method get_default which returns safe values
    for layout parameters. It can be overwritten for specific formats.

    Debugging
    ---------
    There is a set of debug flags (starting with TRACE_) that can be set to 1;
    after recompiling you will get additional output. Since this class is not
    a descendant of device_t we do not have a tag for output; for a better
    overview in the logfile the hard disk device passes its tag to the base
    class.


    TODO
    ----
    - Add ECC computation


    Michael Zapf
    August 2015

**************************************************************************/

#include "emu.h"
#include "mfm_hd.h"
#include "imageutl.h"

#define TRACE_RWTRACK 0
#define TRACE_LAYOUT 0
#define TRACE_IMAGE 0
#define TRACE_DETAIL 0
#define TRACE_FORMAT 0

/*
    Accept the new layout parameters and reset the sector number fields
    used for skew calculation.
*/
void mfmhd_image_format_t::set_layout_params(mfmhd_layout_params param)
{
	m_param = m_param_old = param;
	m_secnumber[0] = m_secnumber[1] = m_secnumber[2] = -1;
}

/*
    Encode some value with data-type clock bits.
*/
void mfmhd_image_format_t::mfm_encode(uint16_t* trackimage, int& position, uint8_t byte, int count)
{
	mfm_encode_mask(trackimage, position, byte, count, 0x00);
}

/*
    Encode an A1 value with mark-type clock bits.
*/
void mfmhd_image_format_t::mfm_encode_a1(uint16_t* trackimage, int& position)
{
	m_current_crc = 0xffff;
	mfm_encode_mask(trackimage, position, 0xa1, 1, 0x04);
}

/*
    Encode a byte value with a given clock bit mask. Used by both mfm_encode
    and mfm_encode_a1 methods.
*/
void mfmhd_image_format_t::mfm_encode_mask(uint16_t* trackimage, int& position, uint8_t byte, int count, int mask)
{
	uint16_t encclock = 0;
	uint16_t encdata = 0;
	uint8_t thisbyte = byte;
	bool mark = (mask != 0x00);

	m_current_crc = ccitt_crc16_one(m_current_crc, byte);

	for (int i=0; i < 8; i++)
	{
		encdata <<= 1;
		encclock <<= 1;

		if (m_param.encoding == MFM_BITS || m_param.encoding == MFM_BYTE)
		{
			// skip one position for later interleaving
			encdata <<= 1;
			encclock <<= 1;
		}

		if (thisbyte & 0x80)
		{
			// Encoding 1 => 01
			encdata |= 1;
			m_lastbit = true;
		}
		else
		{
			// Encoding 0 => x0
			// If the bit in the mask is set, suppress the clock bit
			// Also, if we use the simplified encoding, don't set the clock bits
			if (m_lastbit == false && m_param.encoding != SEPARATED_SIMPLE && (mask & 0x80) == 0) encclock |= 1;
			m_lastbit = false;
		}
		mask <<= 1;
		// For simplified encoding, set all clock bits to indicate a mark
		if (m_param.encoding == SEPARATED_SIMPLE && mark) encclock |= 1;
		thisbyte <<= 1;
	}

	if (m_param.encoding == MFM_BITS || m_param.encoding == MFM_BYTE)
		encclock <<= 1;
	else
		encclock <<= 8;

	trackimage[position++] = (encclock | encdata);

	// When we write the byte multiple times, check whether the next encoding
	// differs from the previous because of the last bit

	if (m_param.encoding == MFM_BITS || m_param.encoding == MFM_BYTE)
	{
		encclock &= 0x7fff;
		if ((byte & 0x80)==0 && m_lastbit==false) encclock |= 0x8000;
	}

	for (int j=1; j < count; j++)
	{
		trackimage[position++] = (encclock | encdata);
		m_current_crc = ccitt_crc16_one(m_current_crc, byte);
	}
}

/*
    Decode an MFM cell pattern into a byte value.
    Clock bits and data bits are assumed to be interleaved (cdcdcdcdcdcdcdcd);
    the 8 data bits are returned.
*/
uint8_t mfmhd_image_format_t::mfm_decode(uint16_t raw)
{
	unsigned int value = 0;

	for (int i=0; i < 8; i++)
	{
		value <<= 1;

		value |= (raw & 0x4000);
		raw <<= 2;
	}
	return (value >> 14) & 0xff;
}

/*
    For debugging. Outputs the byte array in a xxd-like way.
*/
void mfmhd_image_format_t::showtrack(uint16_t* enctrack, int length)
{
	for (int i=0; i < length; i+=16)
	{
		osd_printf_verbose("%07x: ", i);
		for (int j=0; j < 16; j++)
		{
			osd_printf_verbose("%04x ", enctrack[i+j]);
		}
		osd_printf_verbose(" ");
		osd_printf_verbose("\n");
	}
}

// ======================================================================
//    Generic MFM HD format
// ======================================================================

const mfmhd_format_type MFMHD_GEN_FORMAT = &mfmhd_image_format_creator<mfmhd_generic_format>;

/*
    Calculate the ident byte from the cylinder. The specification does not
    define idents beyond cylinder 1023, but formatting programs seem to
    continue with 0xfd for cylinders between 1024 and 2047.
*/
uint8_t mfmhd_generic_format::cylinder_to_ident(int cylinder)
{
	if (cylinder < 256) return 0xfe;
	if (cylinder < 512) return 0xff;
	if (cylinder < 768) return 0xfc;
	return 0xfd;
}

/*
    Returns the linear sector number, given the CHS data.

      C,H,S
    | 0,0,0 | 0,0,1 | 0,0,2 | ...
    | 0,1,0 | 0,1,1 | 0,1,2 | ...
    ...
    | 1,0,0 | ...
    ...
*/
int mfmhd_generic_format::chs_to_lba(int cylinder, int head, int sector)
{
	if ((cylinder < m_param.cylinders) && (head < m_param.heads) && (sector < m_param.sectors_per_track))
	{
		return (cylinder * m_param.heads + head) * m_param.sectors_per_track + sector;
	}
	else return -1;
}

chd_error mfmhd_generic_format::load(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head)
{
	chd_error state = CHDERR_NONE;
	uint8_t sector_content[16384];

	int sectorcount = m_param.sectors_per_track;
	int size = m_param.sector_size;
	int position = 0; // will be incremented by each encode call
	int sec_number = 0;
	int identfield = 0;
	int cylfield = 0;
	int headfield = 0;
	int sizefield = (size >> 7)-1;

	// If we don't have interleave data in the CHD, take a default
	if (m_param.interleave==0)
	{
		m_param.interleave = get_default(MFMHD_IL);
		m_param.cylskew = get_default(MFMHD_CSKEW);
		m_param.headskew = get_default(MFMHD_HSKEW);
	}

	int sec_il_start = (m_param.cylskew * cylinder + m_param.headskew * head) % sectorcount;
	int delta = (sectorcount + m_param.interleave-1) / m_param.interleave;

	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);

	m_lastbit = false;

	if (m_param.sync==0)
	{
		m_param.gap1 = get_default(MFMHD_GAP1);
		m_param.gap2 = get_default(MFMHD_GAP2);
		m_param.gap3 = get_default(MFMHD_GAP3);
		m_param.sync = get_default(MFMHD_SYNC);
		m_param.headerlen = get_default(MFMHD_HLEN);
		m_param.ecctype = get_default(MFMHD_ECC);
	}

	// Gap 1
	mfm_encode(trackimage, position, 0x4e, m_param.gap1);

	if (TRACE_LAYOUT) osd_printf_verbose("%s: cyl=%d head=%d: sector sequence = ", tag(), cylinder, head);

	sec_number = sec_il_start;
	for (int sector = 0; sector < sectorcount; sector++)
	{
		if (TRACE_LAYOUT) osd_printf_verbose("%02d ", sec_number);

		// Sync gap
		mfm_encode(trackimage, position, 0x00, m_param.sync);

		// Write IDAM
		mfm_encode_a1(trackimage, position);

		// Write header
		identfield = cylinder_to_ident(cylinder);
		cylfield = cylinder & 0xff;
		headfield = head & 0x0f;
		if (m_param.headerlen==5)
			headfield |= ((cylinder & 0x700)>>4);

		mfm_encode(trackimage, position, identfield);
		mfm_encode(trackimage, position, cylfield);
		mfm_encode(trackimage, position, headfield);
		mfm_encode(trackimage, position, sec_number);
		if (m_param.headerlen==5)
			mfm_encode(trackimage, position, sizefield);

		// Write CRC for header.
		int crc = m_current_crc;
		mfm_encode(trackimage, position, (crc >> 8) & 0xff);
		mfm_encode(trackimage, position, crc & 0xff);

		// Gap 2
		mfm_encode(trackimage, position, 0x4e, m_param.gap2);

		// Sync
		mfm_encode(trackimage, position, 0x00, m_param.sync);

		// Write DAM
		mfm_encode_a1(trackimage, position);
		mfm_encode(trackimage, position, 0xfb);

		// Get sector content from CHD
		int lbaposition = chs_to_lba(cylinder, head, sec_number);
		if (lbaposition>=0)
		{
			chd_error state = chdfile->read_units(lbaposition, sector_content);
			if (state != CHDERR_NONE) break;
		}
		else
		{
			osd_printf_verbose("%s: Invalid CHS data (%d,%d,%d); not loading from CHD\n", tag(), cylinder, head, sector);
		}

		for (int i=0; i < size; i++)
			mfm_encode(trackimage, position, sector_content[i]);

		// Write CRC for content.
		crc = m_current_crc;
		mfm_encode(trackimage, position, (crc >> 8) & 0xff);
		mfm_encode(trackimage, position, crc & 0xff);

		// Gap 3
		mfm_encode(trackimage, position, 0x00, 3);
		mfm_encode(trackimage, position, 0x4e, m_param.gap3-3);

		// Calculate next sector number
		sec_number += delta;
		if (sec_number >= sectorcount)
		{
			sec_il_start = (sec_il_start+1) % delta;
			sec_number = sec_il_start;
		}
	}
	if (TRACE_LAYOUT) osd_printf_verbose("\n");

	// Gap 4
	if (state == CHDERR_NONE)
	{
		// Fill the rest with 0x4e
		mfm_encode(trackimage, position, 0x4e, tracksize-position);
		if (TRACE_IMAGE) showtrack(trackimage, tracksize);
	}
	return state;
}

/*
    State names for analyzing the track image.
*/
enum
{
	SEARCH_A1=0,
	FOUND_A1,
	DAM_FOUND,
	CHECK_CRC
};

chd_error mfmhd_generic_format::save(chd_file* chdfile, uint16_t* trackimage, int tracksize, int current_cylinder, int current_head)
{
	if (TRACE_RWTRACK) osd_printf_verbose("%s: write back (c=%d,h=%d) to CHD\n", tag(), current_cylinder, current_head);

	uint8_t buffer[16384]; // for header or sector content

	int bytepos = 0;
	int state = SEARCH_A1;
	int count = 0;
	int pos = 0;
	uint16_t crc = 0;
	uint8_t byte;
	bool search_header = true;

	int ident = 0;
	int cylinder = 0;
	int head = 0;
	int sector = 0;
	int size = 0;

	int headerpos = 0;

	int interleave = 0;
	int interleave_prec = -1;
	bool check_interleave = true;
	bool check_skew = true;

	int gap1 = 0;
	int ecctype = 0;

	// if (current_cylinder==0 && current_head==0) showtrack(trackimage, tracksize);

	// If we want to detect gaps, we only do it on cylinder 0, head 0
	// This makes it safer to detect the header length
	// (There is indeed some chance that we falsely assume a header length of 4
	// because the two bytes behind happen to be a valid CRC value)
	if (save_param(MFMHD_GAP1) && current_cylinder==0 && current_head==0)
	{
		m_param.gap1 = 0;
		m_param.gap2 = 0;
		m_param.gap3 = 0;
		m_param.sync = 0;
		// 4-byte headers are used for the IBM-AT format
		// 5-byte headers are used in other formats
		m_param.headerlen = 4;
		m_param.ecctype = 0;
	}

	// AT format implies 512 bytes per sector
	int sector_length = 512;

	// Only check once
	bool countgap1 = (m_param.gap1==0);
	bool countgap2 = false;
	bool countgap3 = false;
	bool countsync = false;

	chd_error chdstate = CHDERR_NONE;

	if (TRACE_IMAGE)
	{
		for (int i=0; i < tracksize; i++)
		{
			if ((i % 16)==0) osd_printf_verbose("\n%04x: ", i);
			osd_printf_verbose("%02x ", (m_param.encoding==MFM_BITS || m_param.encoding==MFM_BYTE)? mfm_decode(trackimage[i]) : (trackimage[i]&0xff));
		}
		osd_printf_verbose("\n");
	}

	// We have to go through the bytes of the track and save a sector as soon as one shows up

	while (bytepos < tracksize)
	{
		// Decode the next 16 bits
		if (m_param.encoding==MFM_BITS || m_param.encoding==MFM_BYTE)
		{
			byte = mfm_decode(trackimage[bytepos]);
		}
		else byte = (trackimage[bytepos] & 0xff);

		switch (state)
		{
		case SEARCH_A1:
			// Counting gaps and sync
			if (countgap2)
			{
				if (byte == 0x4e) m_param.gap2++;
				else if (byte == 0) { countsync = true; countgap2 = false; }
			}

			if (countsync)
			{
				if (byte == 0) m_param.sync++;
				else countsync = false;
			}

			if (countgap3)
			{
				if (byte != 0x00 || m_param.gap3 < 4) m_param.gap3++;
				else countgap3 = false;
			}

			if (((m_param.encoding==MFM_BITS || m_param.encoding==MFM_BYTE) && trackimage[bytepos]==0x4489)
				|| (m_param.encoding==SEPARATED && trackimage[bytepos]==0x0aa1)
				|| (m_param.encoding==SEPARATED_SIMPLE && trackimage[bytepos]==0xffa1))
			{
				state = FOUND_A1;
				count = (search_header? m_param.headerlen : (sector_length+1)) + 2;
				crc = 0x443b; // init value with a1
				pos = 0;
			}
			bytepos++;
			break;

		case FOUND_A1:
			crc = ccitt_crc16_one(crc, byte);
			// osd_printf_verbose("%s: MFM HD: Byte = %02x, CRC=%04x\n", tag(), byte, crc);

			// Put byte into buffer
			// but not the data mark and the CRC
			if (search_header || (count > 2 &&  count < sector_length+3)) buffer[pos++] = byte;

			// Stop counting gap1
			if (search_header && countgap1)
			{
				gap1 = bytepos-1;
				countgap1 = false;
			}

			if (--count == 0)
			{
				if (crc==0)
				{
					if (search_header)
					{
						// Found a header
						ident = buffer[0];
						cylinder = buffer[1];
						// For non-PC-AT formats, highest three bits are in the head field
						if (m_param.headerlen == 5) cylinder |= ((buffer[2]&0x70)<<4);
						else
						{
							osd_printf_verbose("%s: Unexpected header size: %d, cylinder=%d, position=%04x\n", tag(), m_param.headerlen, cylinder, bytepos);
							showtrack(trackimage, tracksize);
						}

						head = buffer[2] & 0x0f;
						sector = buffer[3];
						int identexp = cylinder_to_ident(cylinder);

						if (identexp != ident)
						{
							osd_printf_verbose("%s: Field error; ident = %02x (expected %02x) for sector chs=(%d,%d,%d)\n", tag(), ident, identexp, cylinder, head, sector);
						}

						if (cylinder != current_cylinder)
						{
							osd_printf_verbose("%s: Sector header of sector %d defines cylinder = %02x (should be %02x)\n", tag(), sector, cylinder, current_cylinder);
						}

						if (head != current_head)
						{
							osd_printf_verbose("%s: Sector header of sector %d defines head = %02x (should be %02x)\n", tag(), sector, head, current_head);
						}

						// Check skew
						// We compare the beginning of this track with the track on the next head and the track on the next cylinder
						if (check_skew && cylinder < 2 && head < 2)
						{
							m_secnumber[cylinder*2 + head] = sector;
							check_skew=false;
						}

						// Count the sectors for the interleave
						if (check_interleave)
						{
							if (interleave_prec == -1) interleave_prec = sector;
							else
							{
								if (sector == interleave_prec+1) check_interleave = false;
								interleave++;
							}
						}

						if (interleave == 0) interleave = sector - buffer[3];

						// When we have 4-byte headers, the sector length is 512 bytes
						if (m_param.headerlen == 5)
						{
							size = buffer[4];
							sector_length = 128 << (size&0x07);
							ecctype = (size&0xf0)>>4;
						}

						search_header = false;
						if (TRACE_DETAIL) osd_printf_verbose("%s: Found sector chs=(%d,%d,%d)\n", tag(), cylinder, head, sector);
						headerpos = pos;
						// Start the GAP2 counter (if not already determined)
						if (m_param.gap2==0) countgap2 = true;
					}
					else
					{
						// Sector contents
						// Write the sectors to the CHD
						int lbaposition = chs_to_lba(cylinder, head, sector);
						if (lbaposition>=0)
						{
							if (TRACE_DETAIL) osd_printf_verbose("%s: Writing sector chs=(%d,%d,%d) to CHD\n", tag(), current_cylinder, current_head, sector);
							chdstate = chdfile->write_units(chs_to_lba(current_cylinder, current_head, sector), buffer);

							if (chdstate != CHDERR_NONE)
							{
								osd_printf_verbose("%s: Write error while writing sector chs=(%d,%d,%d)\n", tag(), cylinder, head, sector);
							}
						}
						else
						{
							osd_printf_verbose("%s: Invalid CHS data in track image: (%d,%d,%d); not saving to CHD\n", tag(), cylinder, head, sector);
						}
						if (m_param.gap3==0) countgap3 = true;
						search_header = true;
					}
				}
				else
				{
					// Let's test for a 5-byte header
					if (search_header && m_param.headerlen==4 && current_cylinder==0 && current_head==0)
					{
						if (TRACE_DETAIL) osd_printf_verbose("%s: CRC error for 4-byte header; trying 5 bytes\n", tag());
						m_param.headerlen=5;
						count = 1;
						bytepos++;
						break;
					}
					else
					{
						osd_printf_verbose("%s: CRC error in %s of (%d,%d,%d)\n", tag(), search_header? "header" : "data", cylinder, head, sector);
						search_header = true;
					}
				}
				// search next A1
				state = SEARCH_A1;

				if (!search_header && (pos - headerpos) > 30)
				{
					osd_printf_verbose("%s: Error; missing DAM; searching next header\n", tag());
					search_header = true;
				}
			}
			bytepos++;
			break;
		}
	}

	if (check_interleave == false && save_param(MFMHD_IL))
	{
		// Successfully determined the interleave
		m_param.interleave = interleave;
		if (TRACE_FORMAT)
			if (current_cylinder==0 && current_head==0) osd_printf_verbose("%s: Determined interleave = %d\n", tag(), m_param.interleave);
	}

	if (check_skew == false)
	{
		if (m_secnumber[0] != -1)
		{
			if (m_secnumber[1] != -1)
			{
				if (save_param(MFMHD_HSKEW)) m_param.headskew = m_secnumber[1]-m_secnumber[0];
				if (TRACE_FORMAT) osd_printf_verbose("%s: Determined head skew = %d\n", tag(), m_param.headskew);
			}
			if (m_secnumber[2] != -1)
			{
				if (save_param(MFMHD_CSKEW)) m_param.cylskew = m_secnumber[2]-m_secnumber[0];
				if (TRACE_FORMAT) osd_printf_verbose("%s: Determined cylinder skew = %d\n", tag(), m_param.cylskew);
			}
		}
	}

	gap1 -= m_param.sync;
	ecctype = -1;   // lock to CRC until we have a support for ECC

	if (current_cylinder==0 && current_head==0)
	{
		// If we want to detect gaps, store the new value into the param object
		// The other gaps have already been written directly to the param object above,
		// unless save_param returned false (or we were not on cylinder 0, head 0)
		if (save_param(MFMHD_GAP1)) m_param.gap1 = gap1;
		if (save_param(MFMHD_ECC)) m_param.ecctype = ecctype;
	}
	return chdstate;
}

/*
    Deliver default values.
*/
int mfmhd_generic_format::get_default(mfmhd_param_t type)
{
	switch (type)
	{
	case MFMHD_IL: return 4;
	case MFMHD_HSKEW:
	case MFMHD_CSKEW: return 0;
	case MFMHD_WPCOM:               // Write precompensation cylinder (-1 = none)
	case MFMHD_RWC: return -1;      // Reduced write current cylinder (-1 = none)
	case MFMHD_GAP1: return 16;
	case MFMHD_GAP2: return 3;
	case MFMHD_GAP3: return 18;
	case MFMHD_SYNC: return 13;
	case MFMHD_HLEN: return 5;
	case MFMHD_ECC: return -1;      // -1: use CRC instead of ECC
	}
	return -1;
}