summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/hxchfe_dsk.cpp
blob: 57990c606d62531dc23e9e35910e1228c53197dc (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
// license:BSD-3-Clause
// copyright-holders:Michael Zapf

/*
    The hfe_format class implements the HFE format that is used for the Lotharek
    floppy emulator.

    Format definition according to the official document:

    File header (0x0000 - 0x01FF, 512 bytes)
    ----------------------------------------

    typedef struct picfileformatheader_
    {
       uint8_t HEADERSIGNATURE[8];        // 0: "HXCPICFE"
       uint8_t formatrevision;            // 8: Revision 0
       uint8_t number_of_track;           // 9: Number of track in the file
       uint8_t number_of_side;            // 10: Number of valid side (Not used by the emulator)
       uint8_t track_encoding;            // 11: Track Encoding mode
                                        // (Used for the write support - Please see the list above)
       uint16_t bitRate;                  // 12: Bitrate in Kbit/s. Ex : 250=250000bits/s
                                        // Max value : 500
       uint16_t floppyRPM;                // 14: Rotation per minute (Not used by the emulator)
       uint8_t floppyinterfacemode;       // 16: Floppy interface mode. (Please see the list above.)
       uint8_t dnu;                       // 17: Free
       uint16_t track_list_offset;        // 18: Offset of the track list LUT in block of 512 bytes
                                        // (Ex: 1=0x200)
       uint8_t write_allowed;             // 20: The Floppy image is write protected ?
       uint8_t single_step;               // 21: 0xFF : Single Step – 0x00 Double Step mode
       uint8_t track0s0_altencoding;      // 22: 0x00 : Use an alternate track_encoding for track 0 Side 0
       uint8_t track0s0_encoding;         // 23: alternate track_encoding for track 0 Side 0
       uint8_t track0s1_altencoding;      // 24: 0x00 : Use an alternate track_encoding for track 0 Side 1
       uint8_t track0s1_encoding;         // 25: alternate track_encoding for track 0 Side 1
    } picfileformatheader;

    Byte order for uint16_t is little endian.

    floppyintefacemode values are defined in the header file as floppymode_t,
    track_encodings are defined as encoding_t

    track0s0_encoding is only valid when track0s0_altencoding==0xff
    track0s1_encoding is only valid when track0s1_altencoding==0xff

    Track offset lookup table (at 0x0200)
    -------------------------------------

    typedef struct pictrack_
    {
        uint16_t offset;     // Offset of the track data in blocks of 512 bytes (Ex: 2=0x400)
        uint16_t track_len;  // Length of the track data in byte.
    } pictrack;

    This table has a size of  number_of_track*4 bytes.

    Track data
    ----------

    (first possible occurance at 0x0400)

    Each track is encoded in a sequence of cell levels which are represented
    by bits in the data.

     +--------+--------+--------+--------+---- ........ ---+--------+--------+
     | Head 0 | Head 1 | Head 0 | Head 1 | Hea ........  1 | Head 0 | Head 1 |
     +--------+--------+--------+--------+---- ........ ---+--------+--------+
     |     Block 0     |     Block 1     |                 |    Block n-1    |

    Each block (Head 0 + Head 1) is 0x200 bytes long, with 0x100 bytes for
    each head. Block n-1 may be partially filled, e.g. with 64 bytes for
    head 0 and 64 bytes for head 1. The contents for head 1 in block n-1
    start at offxet 0x100 nevertheless:

     +--------+--------+
     |]]]]  0 |]]]]  1 |
     +--------+--------+
     |    Block n-1    |


    Each byte in the track data is a sequence of cell sample levels
    according to the sample rate. Bit order is little endian:

     Bits
     7 6 5 4 3 2 1 0   15 14 13 12 11 10 9 8   23 22 21 20 19 18 17 16

     0-bits indicate no change, 1-bits indicate flux level change.

    To encode the byte 0x4e in MFM at 250 kbit/s, the following data bytes
    are used:

     Byte:          0  1  0  0  1  1  1  0
     MDM encoding: 10 01 00 10 01 01 01 00
     Reversed order: 0010 1010 0100 1001     = 2a 49

    Interestingly, FM-encoded disks are usually sampled at 250 kbit/s,
    like MFM, although FM only delivers 125 kbit/s. This oversampling leads
    to zero bits (no change) every two positions. See below for details.

     TODO:
     - Handle double-stepping for medium.tracks=40, drive.tracks=80
*/

#include "hxchfe_dsk.h"

#define HEADER_LENGTH 512
#define TRACK_TABLE_LENGTH 1024

hfe_format::hfe_format() : floppy_image_format_t(),
   m_cylinders(0),
   m_heads(0),
   m_track_encoding(UNKNOWN_ENCODING),
   m_bit_rate(0),
   m_floppy_rpm(0),
   m_interface_mode(DISABLE_FLOPPYMODE),
   m_write_allowed(true),
   m_single_step(true),
   m_track0s0_has_altencoding(false),
   m_track0s0_encoding(UNKNOWN_ENCODING),
   m_track0s1_has_altencoding(false),
   m_track0s1_encoding(UNKNOWN_ENCODING),
   m_selected_mode(DISABLE_FLOPPYMODE),
   m_selected_encoding(UNKNOWN_ENCODING)
{
}

const char *hfe_format::name() const
{
	return "hfe";
}

const char *hfe_format::description() const
{
	return "SDCard HxC Floppy Emulator HFE File format";
}

const char *hfe_format::extensions() const
{
	return "hfe";
}

bool hfe_format::supports_save() const
{
	return true;
}

int hfe_format::identify(io_generic *io, uint32_t form_factor)
{
	uint8_t header[8];

	io_generic_read(io, &header, 0, sizeof(header));
	if ( memcmp( header, HFE_FORMAT_HEADER, 8 ) ==0) {
		return 100;
	}
	return 0;
}

bool hfe_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
	uint8_t header[HEADER_LENGTH];
	uint8_t track_table[TRACK_TABLE_LENGTH];

	int drivecyl, driveheads;
	image->get_maximal_geometry(drivecyl, driveheads);

	// read header
	io_generic_read(io, header, 0, HEADER_LENGTH);

	// get values
	// Format revision must be 0
	if (header[8] != 0)
	{
		osd_printf_error("hxchfe: Invalid format revision. Expected 0, got %d.\n", header[8]);
		return false;
	}

	m_cylinders = header[9] & 0xff;
	m_heads = header[10] & 0xff;

	if (drivecyl < m_cylinders)
	{
		osd_printf_error("hxchfe: Floppy disk has too many tracks for this drive (floppy tracks=%d, drive tracks=%d).\n", m_cylinders, drivecyl);
		return false;
	}

	if (m_cylinders <= drivecyl/2)
	{
		osd_printf_error("hxchfe: Double stepping not yet supported (floppy tracks=%d, drive tracks=%d).\n", m_cylinders, drivecyl);
		return false;
	}

	m_track_encoding = (encoding_t)(header[11] & 0xff);

	if (m_track_encoding > EMU_FM_ENCODING)
	{
		osd_printf_error("hxchfe: Unknown track encoding %d.\n", m_track_encoding);
		return false;
	}

	m_bit_rate = (header[12] & 0xff) | ((header[13] & 0xff)<<8);

	if (m_bit_rate > 500)
	{
		osd_printf_error("hxchfe: Unsupported bit rate %d.\n", m_bit_rate);
		return false;
	}
	int samplelength = 500000 / m_bit_rate;

	// Not used in the HxC emulator
	m_floppy_rpm = (header[14] & 0xff) | ((header[15] & 0xff)<<8);

	m_interface_mode = (floppymode_t)(header[16] & 0xff);
	if (m_interface_mode > S950_HD_FLOPPYMODE)
	{
		osd_printf_error("hxchfe: Unknown interface mode %d.\n", m_interface_mode);
		return false;
	}

	m_write_allowed = (header[20] != 0);
	m_single_step = (header[21] != 0);
	m_track0s0_has_altencoding = (header[22] == 0x00);
	m_track0s0_encoding = (encoding_t)(header[23] & 0xff);
	m_track0s1_has_altencoding = (header[24] == 0x00);
	m_track0s1_encoding = (encoding_t)(header[25] & 0xff);

	// read track lookup table (multiple of 512)
	int table_offset = (header[18] & 0xff) | ((header[19] & 0xff)<<8);

	io_generic_read(io, track_table, table_offset<<9, TRACK_TABLE_LENGTH);

	for (int i=0; i < m_cylinders; i++)
	{
		m_cyl_offset[i] = (track_table[4*i] & 0xff) | ((track_table[4*i+1] & 0xff)<<8);
		m_cyl_length[i] = (track_table[4*i+2] & 0xff) | ((track_table[4*i+3] & 0xff)<<8);
	}

	// Load the tracks
	std::vector<uint8_t> cylinder_buffer;
	for(int cyl=0; cyl < m_cylinders; cyl++)
	{
		// actual data read
		// The HFE format defines an interleave of the two sides per cylinder
		// at every 256 bytes
		cylinder_buffer.resize(m_cyl_length[cyl]);
		io_generic_read(io, &cylinder_buffer[0], m_cyl_offset[cyl]<<9, m_cyl_length[cyl]);

		generate_track_from_hfe_bitstream(cyl, 0, samplelength, &cylinder_buffer[0], m_cyl_length[cyl], image);
		if (m_heads == 2)
			generate_track_from_hfe_bitstream(cyl, 1, samplelength, &cylinder_buffer[0], m_cyl_length[cyl], image);
	}

	bool success = true;

	// Find variant
	if (m_track_encoding == ISOIBM_FM_ENCODING || m_track_encoding == EMU_FM_ENCODING)
		// FM is for single density
		image->set_variant((m_heads==1)? floppy_image::SSSD : floppy_image::DSSD);
	else
	{
		// MFM encoding is for everything else
		if (m_track_encoding == ISOIBM_MFM_ENCODING || m_track_encoding == AMIGA_MFM_ENCODING)
		{
			// Each cylinder contains the samples of both sides, 8 samples per
			// byte; the bitRate determines how many samples constitute a cell

			// DSDD: 360 KiB (5.25")= 2*40*18*256; 100000 cells/track, 2 us, bit rate = 250 kbit/s
			// DSDD: 720 KiB (3.5") = 2*80*18*256; 100000 cells/track, 2 us, 250 kbit/s
			// DSHD: 1.4 MiB = 2*80*18*512 bytes; 200000 cells/track, 1 us, 500 kbit/s
			// DSED: 2.8 MiB = 2*80*36*512 bytes; 400000 cells/track, 500 ns, 1 Mbit/s

			// Use cylinder 1 (cyl 0 may have special encodings)
			int cellcount = (m_cyl_length[1] * 8 / 2) * 250 / m_bit_rate;
			if (cellcount > 300000)
				image->set_variant(floppy_image::DSED);
			else
			{
				if (cellcount > 150000)
					image->set_variant(floppy_image::DSHD);
				else
				{
					if (cellcount > 90000)
						// We cannot distinguish DSDD from DSQD without knowing the size of the floppy disk
						image->set_variant((m_heads==1)? floppy_image::SSDD : floppy_image::DSDD);
				}
			}
		}
		else
			success = false;
	}
	return success;
}

void hfe_format::generate_track_from_hfe_bitstream(int cyl, int head, int samplelength, const uint8_t *trackbuf, int track_end, floppy_image *image)
{
	// HFE has a minor issue: The track images do not sum up to 200 ms.
	// Tracks are samples at 250 kbit/s for both FM and MFM, which yields
	// 50000 data bits (100000 samples) for MFM, while FM is twice oversampled
	// (4 samples per actual data bit)
	// Hence, for both FM and MFM, we need 100000 samples.

	// Track length 61B0 (both sides, FM)
	// 100 + 100 + ... + 100 + (B0+50)    = 3000 + B0 + 50 (pad)
	//    100 + 100 + .... + 100  +  B0   = 3000 + B0 = 99712 samples   (-288)

	// Track length 61C0 (both sides, MFM)
	// 100 + 100 + ... + 100 + (C0+40)       = 3000 + C0 + 40 (pad)
	//    100 + 100 + .... + 100   +   C0    = 3000 + C0 = 99840 samples   (-160)

	// Solution: Repeat the last byte until we have enough samples
	// Note: We do not call normalize_times here because we're doing the job here

	// HFE does not define subtracks; set to 0

	// MG_1 / MG_0 are (logical) levels that indicate transition / no change
	// MG_A / MG_B are physical flux directions
	//
	// Cell: | AAAABBBB | = MG_1 = | BBBBAAAA |
	//       | AAAAAAAA | = MG_0 = | BBBBBBBB |

	std::vector<uint32_t> &dest = image->get_buffer(cyl, head, 0);
	dest.clear();

	// Start with MG_A
	uint32_t cbit = floppy_image::MG_A;

	int offset = 0x100;

	if (head==0)
	{
		offset = 0;
		track_end -= 0x0100;
	}

	uint8_t current = 0;
	int time  = 0;

	dest.push_back(cbit | time);

	cbit = floppy_image::MG_B;

	// Oversampled FM images (250 kbit/s) start with a 0, where a 1 is
	// expected for 125 kbit/s.
	// In order to make an oversampled image look like a normally sampled one,
	// we position the transition at 500 ns before the cell end.
	// The HFE format has a 1 us minimum cell size; this means that a normally
	// sampled FM image with 11111... at the begining means
	// 125 kbit/s:    1   1   1   1   1...
	// 250 kbit/s:   01  01  01  01  01...
	// 500 kbit/s: 00010001000100010001...
	//
	//   -500             3500            7500            11500
	//     +-|---:---|---:-+ |   :   |   : +-|---:---|---:-+ |
	//     | |   :   |   : | |   :   |   : | |   :   |   : | |
	//     | |   :   |   : +-|---:---|---:-+ |   :   |   : +-|
	//  -500 0      2000    4000    6000    8000   10000   12000
	//
	//  3500 (1)     samplelength - 500
	//  7500 (1)     +samplelength
	// 11500 (1)     +samplelength
	// 15500 (1)     +samplelength
	//
	//  Double samples
	//
	//  1500 (0)    samplelength - 500
	//  3500 (1)    +samplelength
	//  5500 (0)    +samplelength
	//  7500 (1)    +samplelength
	//  9500 (0)    +samplelength
	// 11500 (1)    +samplelength

	time = -500;

	// We are creating a sequence of timestamps with flux info
	// Note that the flux change occurs in the last quarter of a cell

	while (time < 200000000)   // one rotation in nanosec
	{
		current = trackbuf[offset];
		for (int j=0; j < 8; j++)
		{
			time += samplelength;
			if ((current & 1)!=0)
			{
				// Append another transition to the vector
				dest.push_back(cbit | time);

				// Toggle the cell level
				cbit = (cbit == floppy_image::MG_A)? floppy_image::MG_B : floppy_image::MG_A;
			}

			// HFE uses little-endian bit order
			current >>= 1;
		}
		offset++;
		if ((offset & 0xff)==0) offset += 0x100;

		// When we have not reached the track end (after 0.2 sec) but run
		// out of samples, repeat the last value
		if (offset >= track_end) offset = track_end - 1;
	}

	image->set_write_splice_position(cyl, head, 0, 0);
}

bool hfe_format::save(io_generic *io, floppy_image *image)
{
	std::vector<uint8_t> cylbuf;

	// Create a buffer that is big enough to handle HD formats. We don't
	// know the track length until we generate the HFE bitstream.
	cylbuf.resize(0x10000);

	uint8_t header[HEADER_LENGTH];
	uint8_t track_table[TRACK_TABLE_LENGTH];

	int track_end = 0x61c0;
	int samplelength = 2000;

	// Set up header
	const char* sig = "HXCPICFE";
	memcpy(header, sig, 8);

	header[8] = 0;
	// Can we change the number of tracks or heads?
	image->get_actual_geometry(m_cylinders, m_heads);

	header[9] = m_cylinders;
	header[10] = m_heads;
	// Floppy RPM is not used
	header[14] = 0;
	header[15] = 0;

	// Bit rate and encoding will be set later, they may have changed by
	// reformatting. The selected encoding is UNKNOWN_ENCODING unless
	// explicitly set
	m_track_encoding = m_selected_encoding;

	// Take the old mode, unless we have specified a mode
	header[16] = (m_selected_mode != DISABLE_FLOPPYMODE)? m_selected_mode : m_interface_mode;
	header[17] = 0;

	// The track lookup table is located at offset 0x200 (as 512 multiple)
	header[18] = 1;
	header[19] = 0;

	header[20] = m_write_allowed? 0xff : 0x00;
	header[21] = m_single_step? 0xff : 0x00;

	// TODO: Allow for divergent track 0 format
	header[22] = m_track0s0_has_altencoding? 0x00 : 0xff;
	header[23] = m_track0s0_encoding;
	header[24] = m_track0s1_has_altencoding? 0x00 : 0xff;
	header[25] = m_track0s1_encoding;

	// Fill the remaining bytes with 0xff
	for (int i=26; i < HEADER_LENGTH; i++) header[i] = 0xff;

	// Don't write yet; we still have to find out the bit rate.

	// We won't have more than 200000 cells on the track
	for (int cyl=0; cyl < m_cylinders; cyl++)
	{
		// After the call, the encoding will be set to FM or MFM
		generate_hfe_bitstream_from_track(cyl, 0, samplelength, m_track_encoding, &cylbuf[0], track_end, image);
		if (m_heads == 2)
			generate_hfe_bitstream_from_track(cyl, 1, samplelength, m_track_encoding, &cylbuf[0], track_end, image);

		if (cyl==0)
		{
			// Complete the header and write it
			header[11] = m_track_encoding;
			m_bit_rate = 500000/samplelength;
			header[12] = m_bit_rate & 0xff;
			header[13] = (m_bit_rate >> 8) & 0xff;

			// Now write the header
			io_generic_write(io, header, 0, HEADER_LENGTH);

			// Set up the track lookup table
			// We need the encoding value to be sure about the track length
			int len = (m_track_encoding==ISOIBM_FM_ENCODING)? 0x61b0 : 0x61c0;
			int pos = 0x400;

			for (int i=0; i < m_cylinders; i++)
			{
				m_cyl_offset[i] = (pos >> 9);
				m_cyl_length[i] = len;
				pos += (len + 0x1ff) & 0xfe00;
				track_table[i*4] = m_cyl_offset[i] & 0xff;
				track_table[i*4+1] = (m_cyl_offset[i]>>8) & 0xff;
				track_table[i*4+2] = len & 0xff;
				track_table[i*4+3] = (len>>8) & 0xff;
			}
			// Set the remainder to 0xff
			for (int i=m_cylinders*4; i < TRACK_TABLE_LENGTH; i++)
				track_table[i] = 0xff;

			io_generic_write(io, track_table, 0x200, TRACK_TABLE_LENGTH);
		}
		// Write the current cylinder
		io_generic_write(io, &cylbuf[0], m_cyl_offset[cyl]<<9, (m_cyl_length[cyl] + 0x1ff) & 0xfe00);
	}
	return true;
}

void hfe_format::generate_hfe_bitstream_from_track(int cyl, int head, int& samplelength, encoding_t& encoding, uint8_t *cylinder_buffer, int track_end, floppy_image *image)
{
	// We are using an own implementation here because the result of the
	// parent class method would require some post-processing that we
	// can easily avoid.

	// See floppy_image_format_t::generate_bitstream_from_track
	// as the original code

	// No subtracks definded
	std::vector<uint32_t> &tbuf = image->get_buffer(cyl, head, 0);
	if (tbuf.size() <= 1)
	{
		// Unformatted track
		// TODO must handle that according to HFE
		int track_size = 200000000/samplelength;
		memset(cylinder_buffer, 0, (track_size+7)/8);
		return;
	}

	// Find out whether we have FM or MFM recording, and determine the bit rate.
	// This is needed for the format header.
	//
	// The encoding may have changed by reformatting; we cannot rely on the
	// header when loading.
	//
	// FM:   encoding 1    -> flux length = 4 us (min)          ambivalent
	//       encoding 10   -> flux length = 8 us (max)          ambivalent
	// MFM:  encoding 10   -> flux length = 4 us (min, DD)      ambivalent
	//       encoding 100  -> flux length = 6 us (DD)            significant
	//       encoding 1000 -> flux length = 8 us (max, DD)      ambivalent
	//       encoding 10   -> flux length = 2 us (min, HD)       significant
	//       encoding 100  -> flux length = 3 us (max, HD)       significant

	// If we have MFM, we should very soon detect a flux length of 6 us.
	// But if we have FM, how long should we search to be sure?
	// We assume that after 2000 us we should have reached the first IDAM,
	// which contains a sequence 1001, implying a flux length of 6 us.
	// If there was no such flux in that area, this can safely be assumed to be FM.

	// Do it only for the first track; the format only supports one encoding.
	if (encoding == UNKNOWN_ENCODING)
	{
		bool mfm_recording = false;
		int time0 = 0;
		int minflux = 4000;
		int fluxlen = 0;
		// Skip the beginning (may have a short cell)
		for (int i=2; (i < tbuf.size()-1) && (time0 < 2000000) && !mfm_recording; i++)
		{
			time0 = tbuf[i] & floppy_image::TIME_MASK;
			fluxlen = (tbuf[i+1] & floppy_image::TIME_MASK) - time0;
			if ((fluxlen < 3500) || (fluxlen > 5500 && fluxlen < 6500))
				mfm_recording = true;
			if (fluxlen < minflux) minflux = fluxlen;
		}
		encoding = mfm_recording? ISOIBM_MFM_ENCODING : ISOIBM_FM_ENCODING;

		// samplelength = 1000ns => 10^6 cells/sec => 500 kbit/s
		// samplelength = 2000ns => 250 kbit/s
		// We stay with double sampling at 250 kbit/s for FM
		if (minflux < 3500) samplelength = 1000;
		else samplelength = 2000;
	}

	// Start at the write splice
	uint32_t splice = image->get_write_splice_position(cyl, head, 0);

	int cur_pos = splice;
	int cur_entry = 0;

	// Fast-forward to the write splice position (always 0 in this format)
	while (cur_entry < int(tbuf.size())-1 && (tbuf[cur_entry+1] & floppy_image::TIME_MASK) < cur_pos)
		cur_entry++;

	int period = samplelength;
	int period_adjust_base = period * 0.05;

	int min_period = int(samplelength*0.75);
	int max_period = int(samplelength*1.25);
	int phase_adjust = 0;
	int freq_hist = 0;
	uint32_t next = 0;

	int offset = 0x100;

	// Prepare offset for the format storage
	if (head==0)
	{
		offset = 0;
		track_end -= 0x0100;
	}

	uint8_t bit = 0x01;
	uint8_t current = 0;

	while (next < 200000000) {
		int edge = tbuf[cur_entry] & floppy_image::TIME_MASK;

		// Start of track? Use next entry.
		if (edge==0)
		{
			cur_pos = 0;
			edge = tbuf[++cur_entry] & floppy_image::TIME_MASK;
		}

		// Wrapped over end?
		if (edge < cur_pos) edge += 200000000;

		// End of cell
		next = cur_pos + period + phase_adjust;

		// End of the window is at next; edge is the actual transition
		if (edge >= next)
		{
			// No transition in the window -> 0
			phase_adjust = 0;
		}
		else
		{
			// Transition in the window -> 1
			current |= bit;
			int delta = edge - (next - period/2);

			phase_adjust = 0.65*delta;

			if (delta < 0)
			{
				if (freq_hist < 0) freq_hist--;
				else freq_hist = -1;
			}
			else
			{
				if (delta > 0)
				{
					if(freq_hist > 0) freq_hist++;
					else freq_hist = 1;
				}
				else freq_hist = 0;
			}

			if (freq_hist)
			{
				int afh = freq_hist < 0 ? -freq_hist : freq_hist;
				if (afh > 1)
				{
					int aper = period_adjust_base*delta/period;
					if (!aper)
						aper = freq_hist < 0 ? -1 : 1;
					period += aper;

					if (period < min_period) period = min_period;
					else if (period > max_period) period = max_period;
				}
			}
		}

		cur_pos = next;
		if(cur_pos >= 200000000) {
			cur_pos -= 200000000;
			cur_entry = 0;
		}

		bit = (bit << 1) & 0xff;
		if (bit == 0)
		{
			bit = 0x01;
			cylinder_buffer[offset++] = current;
			if ((offset & 0xff)==0) offset += 0x100;
			current = 0;
		}

		// Fast-forward to next cell
		while (cur_entry < int(tbuf.size())-1 && (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos)
			cur_entry++;

		// Reaching the end of the track
		if (cur_entry == int(tbuf.size())-1 &&  (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos)
		{
			// Wrap to index 0 or 1 depending on whether there is a transition exactly at the index hole
			cur_entry = (tbuf[int(tbuf.size())-1] & floppy_image::MG_MASK) != (tbuf[0] & floppy_image::MG_MASK) ?
			0 : 1;
		}
	}
	// Write the current byte when not done
	if (bit != 0x01)
		cylinder_buffer[offset] = current;
}

const floppy_format_type FLOPPY_HFE_FORMAT = &floppy_image_format_creator<hfe_format>;