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
|
// 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"
#include "ioprocs.h"
#include "osdcore.h" // osd_printf_*
#define HFE_FORMAT_HEADER "HXCPICFE"
#define HEADER_LENGTH 512
#define TRACK_TABLE_LENGTH 1024
hfe_format::hfe_format() : floppy_image_format_t()
{
}
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 false;
}
int hfe_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t header[8];
size_t actual;
io.read_at(0, &header, sizeof(header), actual);
if ( memcmp( header, HFE_FORMAT_HEADER, 8 ) ==0) {
return FIFID_SIGN;
}
return 0;
}
bool hfe_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image *image) const
{
size_t actual;
uint8_t header[HEADER_LENGTH];
uint8_t track_table[TRACK_TABLE_LENGTH];
header_info info;
int drivecyl, driveheads;
image->get_maximal_geometry(drivecyl, driveheads);
// read header
io.read_at(0, header, HEADER_LENGTH, actual);
// 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;
}
info.m_cylinders = header[9] & 0xff;
info.m_heads = header[10] & 0xff;
if (drivecyl < info.m_cylinders)
{
if (info.m_cylinders - drivecyl > DUMP_THRESHOLD)
{
osd_printf_error("hxchfe: Floppy disk has too many tracks for this drive (floppy tracks=%d, drive tracks=%d).\n", info.m_cylinders, drivecyl);
return false;
}
else
{
// Some dumps has a few excess tracks to be safe,
// lets be nice and just skip those tracks
osd_printf_warning("hxchfe: Floppy disk has a slight excess of tracks for this drive that will be discarded (floppy tracks=%d, drive tracks=%d).\n", info.m_cylinders, drivecyl);
info.m_cylinders = drivecyl;
}
}
if (info.m_cylinders <= drivecyl/2)
{
osd_printf_error("hxchfe: Double stepping not yet supported (floppy tracks=%d, drive tracks=%d).\n", info.m_cylinders, drivecyl);
return false;
}
info.m_track_encoding = (encoding_t)(header[11] & 0xff);
if (info.m_track_encoding > EMU_FM_ENCODING)
{
osd_printf_error("hxchfe: Unknown track encoding %d.\n", info.m_track_encoding);
return false;
}
info.m_bit_rate = (header[12] & 0xff) | ((header[13] & 0xff)<<8);
if (info.m_bit_rate > 500)
{
osd_printf_error("hxchfe: Unsupported bit rate %d.\n", info.m_bit_rate);
return false;
}
int samplelength = 500000 / info.m_bit_rate;
// Not used in the HxC emulator
info.m_floppy_rpm = (header[14] & 0xff) | ((header[15] & 0xff)<<8);
info.m_interface_mode = (floppymode_t)(header[16] & 0xff);
if (info.m_interface_mode > S950_HD_FLOPPYMODE)
{
osd_printf_error("hxchfe: Unknown interface mode %d.\n", info.m_interface_mode);
return false;
}
info.m_write_allowed = (header[20] != 0);
info.m_single_step = (header[21] != 0);
info.m_track0s0_has_altencoding = (header[22] == 0x00);
info.m_track0s0_encoding = (encoding_t)(header[23] & 0xff);
info.m_track0s1_has_altencoding = (header[24] == 0x00);
info.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.read_at(table_offset<<9, track_table, TRACK_TABLE_LENGTH, actual);
for (int i=0; i < info.m_cylinders; i++)
{
info.m_cyl_offset[i] = (track_table[4*i] & 0xff) | ((track_table[4*i+1] & 0xff)<<8);
info.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 < info.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(info.m_cyl_length[cyl]);
io.read_at(info.m_cyl_offset[cyl]<<9, &cylinder_buffer[0], info.m_cyl_length[cyl], actual);
generate_track_from_hfe_bitstream(cyl, 0, samplelength, &cylinder_buffer[0], info.m_cyl_length[cyl], image);
if (info.m_heads == 2)
generate_track_from_hfe_bitstream(cyl, 1, samplelength, &cylinder_buffer[0], info.m_cyl_length[cyl], image);
}
bool success = true;
// Find variant
if (info.m_track_encoding == ISOIBM_FM_ENCODING || info.m_track_encoding == EMU_FM_ENCODING)
// FM is for single density
image->set_variant((info.m_heads==1)? floppy_image::SSSD : floppy_image::DSSD);
else
{
// MFM encoding is for everything else
if (info.m_track_encoding == ISOIBM_MFM_ENCODING || info.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 = (info.m_cyl_length[1] * 8 / 2) * 250 / info.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((info.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_F is the position of a flux transition
std::vector<uint32_t> &dest = image->get_buffer(cyl, head, 0);
dest.clear();
int offset = 0x100;
if (head==0)
{
offset = 0;
track_end -= 0x0100;
}
uint8_t current = 0;
int time = 0;
// 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(floppy_image::MG_F | time);
// 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);
}
const hfe_format FLOPPY_HFE_FORMAT;
|