summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats
diff options
context:
space:
mode:
author Michael Zapf <Michael.Zapf@mizapf.de>2016-09-18 12:30:34 +0200
committer Michael Zapf <Michael.Zapf@mizapf.de>2016-09-18 12:31:13 +0200
commit2bf93a51e8ea2760fad53dc8c9586ce741d0ea89 (patch)
treef109841b8285f518fc12911476b6101d705edf84 /src/lib/formats
parentb299ad913355d5c5158939af0ad3f9110526d341 (diff)
Added HxC HFE disk format.
Diffstat (limited to 'src/lib/formats')
-rw-r--r--src/lib/formats/hxchfe_dsk.cpp691
-rw-r--r--src/lib/formats/hxchfe_dsk.h91
2 files changed, 782 insertions, 0 deletions
diff --git a/src/lib/formats/hxchfe_dsk.cpp b/src/lib/formats/hxchfe_dsk.cpp
new file mode 100644
index 00000000000..cf959fdff32
--- /dev/null
+++ b/src/lib/formats/hxchfe_dsk.cpp
@@ -0,0 +1,691 @@
+// 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 HEADERSIGNATURE[8]; // 0: "HXCPICFE"
+ UINT8 formatrevision; // 8: Revision 0
+ UINT8 number_of_track; // 9: Number of track in the file
+ UINT8 number_of_side; // 10: Number of valid side (Not used by the emulator)
+ UINT8 track_encoding; // 11: Track Encoding mode
+ // (Used for the write support - Please see the list above)
+ UINT16 bitRate; // 12: Bitrate in Kbit/s. Ex : 250=250000bits/s
+ // Max value : 500
+ UINT16 floppyRPM; // 14: Rotation per minute (Not used by the emulator)
+ UINT8 floppyinterfacemode; // 16: Floppy interface mode. (Please see the list above.)
+ UINT8 dnu; // 17: Free
+ UINT16 track_list_offset; // 18: Offset of the track list LUT in block of 512 bytes
+ // (Ex: 1=0x200)
+ UINT8 write_allowed; // 20: The Floppy image is write protected ?
+ UINT8 single_step; // 21: 0xFF : Single Step – 0x00 Double Step mode
+ UINT8 track0s0_altencoding; // 22: 0x00 : Use an alternate track_encoding for track 0 Side 0
+ UINT8 track0s0_encoding; // 23: alternate track_encoding for track 0 Side 0
+ UINT8 track0s1_altencoding; // 24: 0x00 : Use an alternate track_encoding for track 0 Side 1
+ UINT8 track0s1_encoding; // 25: alternate track_encoding for track 0 Side 1
+ } picfileformatheader;
+
+ Byte order for UINT16 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 offset; // Offset of the track data in blocks of 512 bytes (Ex: 2=0x400)
+ UINT16 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 form_factor)
+{
+ UINT8 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 form_factor, floppy_image *image)
+{
+ UINT8 header[HEADER_LENGTH];
+ UINT8 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
+ dynamic_buffer 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 *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> &dest = image->get_buffer(cyl, head, 0);
+ dest.clear();
+
+ // Start with MG_A
+ UINT32 cbit = floppy_image::MG_A;
+
+ int offset = 0x100;
+
+ if (head==0)
+ {
+ offset = 0;
+ track_end -= 0x0100;
+ }
+
+ UINT8 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)
+{
+ dynamic_buffer cylbuf;
+ cylbuf.resize(0x6200);
+
+ UINT8 header[HEADER_LENGTH];
+ UINT8 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 *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> &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;
+ // Skip the beginning (may have a short cell)
+ for (int i=2; (i < tbuf.size()-1) && (time0 < 2000000) && !mfm_recording; i++)
+ {
+ int time0 = tbuf[i] & floppy_image::TIME_MASK;
+ int 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 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 next = 0;
+
+ int offset = 0x100;
+
+ // Prepare offset for the format storage
+ if (head==0)
+ {
+ offset = 0;
+ track_end -= 0x0100;
+ }
+
+ UINT8 bit = 0x01;
+ UINT8 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>;
+
+
diff --git a/src/lib/formats/hxchfe_dsk.h b/src/lib/formats/hxchfe_dsk.h
new file mode 100644
index 00000000000..4f322f659c3
--- /dev/null
+++ b/src/lib/formats/hxchfe_dsk.h
@@ -0,0 +1,91 @@
+// license:BSD-3-Clause
+// copyright-holders:Michael Zapf
+/*********************************************************************
+
+ formats/hxchfe_dsk.h
+
+ HxC Floppy Emulator HFE file format
+
+*********************************************************************/
+
+#ifndef HXCHFE_DSK_H
+#define HXCHFE_DSK_H
+
+#include "flopimg.h"
+
+#define HFE_FORMAT_HEADER "HXCPICFE"
+
+enum encoding_t
+{
+ ISOIBM_MFM_ENCODING = 0x00,
+ AMIGA_MFM_ENCODING,
+ ISOIBM_FM_ENCODING,
+ EMU_FM_ENCODING,
+ UNKNOWN_ENCODING = 0xff
+};
+
+enum floppymode_t
+{
+ IBMPC_DD_FLOPPYMODE = 00,
+ IBMPC_HD_FLOPPYMODE,
+ ATARIST_DD_FLOPPYMODE,
+ ATARIST_HD_FLOPPYMODE,
+ AMIGA_DD_FLOPPYMODE,
+ AMIGA_HD_FLOPPYMODE,
+ CPC_DD_FLOPPYMODE,
+ GENERIC_SHUGART_DD_FLOPPYMODE,
+ IBMPC_ED_FLOPPYMODE,
+ MSX2_DD_FLOPPYMODE,
+ C64_DD_FLOPPYMODE,
+ EMU_SHUGART_FLOPPYMODE,
+ S950_DD_FLOPPYMODE,
+ S950_HD_FLOPPYMODE,
+ DISABLE_FLOPPYMODE = 0xfe
+};
+
+class hfe_format : public floppy_image_format_t
+{
+public:
+ hfe_format();
+
+ virtual int identify(io_generic *io, UINT32 form_factor) override;
+ virtual bool load(io_generic *io, UINT32 form_factor, floppy_image *image) override;
+ virtual bool save(io_generic *io, floppy_image *image) override;
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+ virtual const char *extensions() const override;
+ virtual bool supports_save() const override;
+
+ void set_floppy_mode(floppymode_t mode) { m_selected_mode = mode; }
+ void set_encoding(encoding_t enc) { m_selected_encoding = enc; }
+
+private:
+ void generate_track_from_hfe_bitstream(int track, int head, int samplelength, const UINT8 *trackbuf, int track_end, floppy_image *image);
+ void generate_hfe_bitstream_from_track(int track, int head, int& samplelength, encoding_t& encoding, UINT8 *trackbuf, int track_end, floppy_image *image);
+
+ // Header fields from the HFE format
+ int m_cylinders; // Number of track in the file
+ int m_heads; // Number of valid side
+ encoding_t m_track_encoding; // Track Encoding mode
+ int m_bit_rate; // Bitrate in Kbit/s (max: 500)
+ int m_floppy_rpm; // Rotation per minute
+ floppymode_t m_interface_mode; // Floppy interface mode.
+
+ bool m_write_allowed;
+ bool m_single_step;
+ bool m_track0s0_has_altencoding;
+ encoding_t m_track0s0_encoding; // alternate track_encoding for track 0 Side 0
+ bool m_track0s1_has_altencoding;
+ encoding_t m_track0s1_encoding; // alternate track_encoding for track 0 Side 1
+
+ int m_cyl_offset[256];
+ int m_cyl_length[256];
+
+ floppymode_t m_selected_mode;
+ encoding_t m_selected_encoding;
+};
+
+extern const floppy_format_type FLOPPY_HFE_FORMAT;
+
+#endif /* HXCHFE_DSK_H */