1 // license:BSD-3-Clause
2 // copyright-holders: Ansgar Kückes, F. Ulivi
3 /*********************************************************************
4
5 hpi_dsk.cpp
6
7 HP9885/HP9895A "HPI" disk images
8
9 CHS = 67/1/30 (SSDD)
10 CHS = 77/2/30 (DSDD)
11 Sector size 256 bytes
12 Cell size 2 µs
13 Gap1 = 16 x 0x00
14 Gap2 = 34 x 0x00
15 Gap3 = ~490 x 0x00 (depends on actual rotation speed)
16 Sync = 4 x 0x00 + 4 x 0xff
17 ID AM = 0x0e clock + 0x70 data
18 Data AM = 0x0e clock + 0x50 data
19 DefectTrack AM = 0x0e clock + 0xf0 data
20 CRC-16 excludes address markers
21 MMFM/M2FM encoding (LS bit first)
22
23 The order of sectors in a track depends on the interleave factor
24 which is the distance (in number of sectors) between two consecutively
25 numbered sectors. Interleave factor is specified at formatting time.
26 The default factor is 7. The order of sectors for this factor is:
27 0, 13, 26, 9, 22, 5, 18, 1, 14, 27, 10, 23, 6, 19, 2,
28 15, 28, 11, 24, 7, 20, 3, 16, 29, 12, 25, 8, 21, 4, 17
29
30 <Track> := [Index hole]|Sector0|Gap2|Sector1|Gap2|...|Sector29|Gap3|
31
32 <Sector> := ID field|Gap1|Data field
33
34 <ID field> := Sync|ID AM|Track no.|Sector no.|CRC-16|0x00
35
36 <Data field> := Sync|Data AM|Data|CRC-16|0x00
37
38 This format is just a raw image of every sector on a HP-formatted
39 8" floppy disk. Files with this format have no header/trailer and
40 are exactly 1182720 bytes in size (30 sectors, 2 heads, 77 tracks,
41 256 bytes per sector). There's also a "reduced" version holding
42 just 75 cylinders.
43 When loading, the disk image is translated to MMFM encoding so
44 that it can be loaded into HP9885/HP9895 emulator.
45
46 *********************************************************************/
47
48 #include "hpi_dsk.h"
49
50 #include "coretmpl.h" // BIT
51
52
53 // Debugging
54 #define VERBOSE 0
55 #define LOG(...) do { if (VERBOSE) osd_printf_info(__VA_ARGS__); } while (false)
56
57 constexpr unsigned IL_OFFSET = 0x12; // Position of interleave factor in HPI image (2 bytes, big-endian)
58 constexpr unsigned DEFAULT_IL = 7; // Default interleaving factor
59 constexpr unsigned CELL_SIZE = 1200; // Bit cell size (1 µs)
60 constexpr uint8_t ID_AM = 0x70; // ID address mark
61 constexpr uint8_t DATA_AM = 0x50; // Data address mark
62 constexpr uint8_t AM_CLOCK = 0x0e; // Clock pattern of AM
63 constexpr uint32_t ID_CD_PATTERN= 0x55552a54; // C/D pattern of 0xff + ID_AM
64 constexpr uint32_t DATA_CD_PATTERN = 0x55552a44; // C/D pattern of 0xff + DATA_AM
65 constexpr unsigned GAP1_SIZE = 17; // Size of gap 1 (+1)
66 constexpr unsigned GAP2_SIZE = 35; // Size of gap 2 (+1)
67 constexpr int ID_DATA_OFFSET = 30 * 16; // Nominal distance (in cells) between ID & DATA AM
68 // Size of image file (holding 77 cylinders)
69 constexpr unsigned HPI_IMAGE_SIZE = HPI_TRACKS * HPI_HEADS * HPI_SECTORS * HPI_SECTOR_SIZE;
70 constexpr unsigned HPI_RED_TRACKS = 75; // Reduced number of tracks
71 constexpr unsigned HPI_9885_TRACKS = 67; // Tracks visible to HP9885 drives
72 // Size of reduced image file (holding 75 cylinders)
73 constexpr unsigned HPI_RED_IMAGE_SIZE = HPI_RED_TRACKS * HPI_HEADS * HPI_SECTORS * HPI_SECTOR_SIZE;
74
hpi_format()75 hpi_format::hpi_format()
76 {
77 (void)HPI_IMAGE_SIZE;
78 (void)HPI_RED_IMAGE_SIZE;
79 }
80
identify(io_generic * io,uint32_t form_factor)81 int hpi_format::identify(io_generic *io, uint32_t form_factor)
82 {
83 uint64_t size = io_generic_size(io);
84
85 // we try to stay back and give only 50 points, since another image
86 // format may also have images of the same size (there is no header and no
87 // magic number for HPI format...
88 unsigned dummy_heads;
89 unsigned dummy_tracks;
90 if (((form_factor == floppy_image::FF_8) || (form_factor == floppy_image::FF_UNKNOWN)) &&
91 geometry_from_size(size , dummy_heads , dummy_tracks)) {
92 return 50;
93 } else {
94 return 0;
95 }
96 }
97
geometry_from_size(uint64_t image_size,unsigned & heads,unsigned & tracks)98 bool hpi_format::geometry_from_size(uint64_t image_size , unsigned& heads , unsigned& tracks)
99 {
100 if ((image_size % HPI_SECTOR_SIZE) != 0) {
101 // Not a whole number of sectors
102 return false;
103 }
104 unsigned sectors = unsigned(image_size / HPI_SECTOR_SIZE);
105 if ((sectors % HPI_SECTORS) != 0) {
106 // Not a whole number of tracks
107 return false;
108 }
109 unsigned tot_tracks = sectors / HPI_SECTORS;
110 // Possible combinations
111 //
112 // | Tot tracks | Heads | Tracks | Format |
113 // |------------+-------+--------+--------|
114 // | 67 | 1 | 67 | SSDD |
115 // | 77 | 1 | 77 | SSDD |
116 // | 150 | 2 | 75 | DSDD |
117 // | 154 | 2 | 77 | DSDD |
118 //
119 switch (tot_tracks) {
120 case HPI_9885_TRACKS:
121 heads = 1;
122 tracks = HPI_9885_TRACKS;
123 return true;
124
125 case HPI_TRACKS:
126 heads = 1;
127 tracks = HPI_TRACKS;
128 return true;
129
130 case HPI_RED_TRACKS * 2:
131 heads = 2;
132 tracks = HPI_RED_TRACKS;
133 return true;
134
135 case HPI_TRACKS * 2:
136 heads = 2;
137 tracks = HPI_TRACKS;
138 return true;
139
140 default:
141 return false;
142 }
143 }
144
load(io_generic * io,uint32_t form_factor,floppy_image * image)145 bool hpi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
146 {
147 unsigned heads;
148 unsigned cylinders;
149
150 uint64_t size = io_generic_size(io);
151 if (!geometry_from_size(size, heads, cylinders)) {
152 return false;
153 }
154 int max_tracks;
155 int max_heads;
156 image->get_maximal_geometry(max_tracks , max_heads);
157 if (cylinders > max_tracks || heads > max_heads) {
158 return false;
159 }
160 image->set_variant(heads == 2 ? floppy_image::DSDD : floppy_image::SSDD);
161
162 // Suck in the whole image
163 std::vector<uint8_t> image_data(size);
164 io_generic_read(io, (void *)image_data.data(), 0, size);
165
166 // Get interleave factor from image
167 unsigned il = (unsigned)image_data[ IL_OFFSET ] * 256 + image_data[ IL_OFFSET + 1 ];
168 LOG("I/L from image: %u\n" , il);
169 if (il < 1 || il >= HPI_SECTORS) {
170 il = DEFAULT_IL;
171 }
172 LOG("Actual I/L: %u\n" , il);
173
174 sector_list_t sector_list;
175 interleaved_sectors(il, sector_list);
176
177 unsigned list_offset = 0;
178 for (unsigned cyl = 0; cyl < cylinders; cyl++) {
179 for (unsigned head = 0; head < heads; head++) {
180 std::vector<uint32_t> track_data;
181 for (unsigned sector = 0; sector < HPI_SECTORS; sector++) {
182 unsigned real_sector = sector_list[ (sector + list_offset) % HPI_SECTORS ];
183 unsigned offset_in_image = chs_to_lba(cyl, head, real_sector, heads) * HPI_SECTOR_SIZE;
184 write_sector(track_data , cyl , real_sector + (head << 7) , &image_data[ offset_in_image ]);
185 }
186 fill_with_gap3(track_data);
187 generate_track_from_levels(cyl , head , track_data , 0 , image);
188 list_offset = (list_offset + m_track_skew[ il - 1 ][ head ]) % HPI_SECTORS;
189 }
190 }
191 return true;
192 }
193
save(io_generic * io,floppy_image * image)194 bool hpi_format::save(io_generic *io, floppy_image *image)
195 {
196 int tracks;
197 int heads;
198 image->get_actual_geometry(tracks, heads);
199
200 for (int cyl = 0; cyl < tracks; cyl++) {
201 for (int head = 0; head < heads; head++) {
202 uint8_t bitstream[ 21000 ];
203 int bitstream_size;
204 generate_bitstream_from_track(cyl , head , CELL_SIZE , bitstream , bitstream_size , image , 0);
205 int pos = 0;
206 unsigned track_no , head_no , sector_no;
207 uint8_t sector_data[ HPI_SECTOR_SIZE ];
208 while (get_next_sector(bitstream , bitstream_size , pos , track_no , head_no , sector_no , sector_data)) {
209 if (track_no == cyl && head_no == head && sector_no < HPI_SECTORS) {
210 unsigned offset_in_image = chs_to_lba(cyl, head, sector_no, heads) * HPI_SECTOR_SIZE;
211 io_generic_write(io, sector_data, offset_in_image, HPI_SECTOR_SIZE);
212 }
213 }
214 }
215 }
216 return true;
217 }
218
name() const219 const char *hpi_format::name() const
220 {
221 return "hpi";
222 }
223
description() const224 const char *hpi_format::description() const
225 {
226 return "HP9895A floppy disk image";
227 }
228
extensions() const229 const char *hpi_format::extensions() const
230 {
231 return "hpi";
232 }
233
supports_save() const234 bool hpi_format::supports_save() const
235 {
236 return true;
237 }
238
interleaved_sectors(unsigned il_factor,sector_list_t & sector_list)239 void hpi_format::interleaved_sectors(unsigned il_factor , sector_list_t& sector_list)
240 {
241 sector_list.fill(0xff);
242
243 unsigned idx = HPI_SECTORS - il_factor;
244 for (unsigned sect = 0; sect < HPI_SECTORS; sect++) {
245 idx = (idx + il_factor) % HPI_SECTORS;
246 while (sector_list[ idx ] != 0xff) {
247 idx = (idx + 1) % HPI_SECTORS;
248 }
249 LOG("[%u]=%u\n" , idx , sect);
250 sector_list[ idx ] = sect;
251 }
252 }
253
write_mmfm_bit(std::vector<uint32_t> & buffer,bool data_bit,bool clock_bit)254 void hpi_format::write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit)
255 {
256 bool had_transition = buffer.size() < 2 ? false : bit_r(buffer, buffer.size() - 1) || bit_r(buffer , buffer.size() - 2);
257 clock_bit = !data_bit && (clock_bit || !had_transition);
258 bit_w(buffer , clock_bit , CELL_SIZE);
259 bit_w(buffer , data_bit , CELL_SIZE);
260 }
261
write_mmfm_byte(std::vector<uint32_t> & buffer,uint8_t data,uint8_t clock)262 void hpi_format::write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint8_t clock)
263 {
264 for (unsigned i = 0; i < 8; i++) {
265 write_mmfm_bit(buffer , util::BIT(data , i) , util::BIT(clock , i));
266 }
267 }
268
write_sync(std::vector<uint32_t> & buffer)269 void hpi_format::write_sync(std::vector<uint32_t> &buffer)
270 {
271 // Sync
272 // 4x 00
273 for (unsigned i = 0; i < 4; i++) {
274 write_mmfm_byte(buffer , 0);
275 }
276 // 4x ff
277 for (unsigned i = 0; i < 4; i++) {
278 write_mmfm_byte(buffer , 0xff);
279 }
280 }
281
write_crc(std::vector<uint32_t> & buffer,uint16_t crc)282 void hpi_format::write_crc(std::vector<uint32_t> &buffer , uint16_t crc)
283 {
284 // Note that CRC is stored with MSB (x^15) first
285 for (unsigned i = 0; i < 16; i++) {
286 write_mmfm_bit(buffer , util::BIT(crc , 15 - i) , 0);
287 }
288 }
289
write_sector(std::vector<uint32_t> & buffer,uint8_t track_no,uint8_t sect_head_no,const uint8_t * sect_data)290 void hpi_format::write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_head_no , const uint8_t *sect_data)
291 {
292 // **** On-disk format of a sector ****
293 //
294 // | Offset | Size | Value | Content |
295 // |--------+------+-------+----------------------|
296 // | 0 | 4 | 00 | Sync |
297 // | 4 | 4 | ff | Sync |
298 // | 8 | 1 | 70 | ID AM (clock = 0e) |
299 // | 9 | 1 | xx | Track no. |
300 // | 10 | 1 | xx | Sector and head no. |
301 // | 11 | 2 | xx | ID CRC |
302 // | 13 | 1 | 00 | ID tail |
303 // | 14 | 16 | 00 | Gap 1 |
304 // | 30 | 4 | 00 | Sync |
305 // | 34 | 4 | ff | Sync |
306 // | 38 | 1 | 50 | Data AM (clock = 0e) |
307 // | 39 | 256 | xx | Sector data |
308 // | 295 | 2 | xx | Data CRC |
309 // | 297 | 1 | 00 | Data tail |
310 // | 298 | 34 | 00 | Gap 2 |
311 // | 332 | | | |
312
313 // Sync
314 write_sync(buffer);
315 // ID AM
316 write_mmfm_byte(buffer , ID_AM , AM_CLOCK);
317 auto crc_start = buffer.size();
318 // Track #
319 write_mmfm_byte(buffer , track_no);
320 // Sector/head #
321 write_mmfm_byte(buffer , sect_head_no);
322 uint16_t crc = calc_crc_ccitt(buffer , crc_start , buffer.size());
323 // ID CRC
324 write_crc(buffer , crc);
325 // Gap 1
326 for (unsigned i = 0; i < GAP1_SIZE; i++) {
327 write_mmfm_byte(buffer , 0);
328 }
329 // Sync
330 write_sync(buffer);
331 // Data AM
332 write_mmfm_byte(buffer , DATA_AM , AM_CLOCK);
333 crc_start = buffer.size();
334 for (unsigned i = 0; i < HPI_SECTOR_SIZE; i += 2) {
335 // Data: bytes are swapped in pairs
336 write_mmfm_byte(buffer , sect_data[ i + 1 ]);
337 write_mmfm_byte(buffer , sect_data[ i ]);
338 }
339 crc = calc_crc_ccitt(buffer , crc_start , buffer.size());
340 // Data CRC
341 write_crc(buffer , crc);
342 // Gap 2
343 for (unsigned i = 0; i < GAP2_SIZE; i++) {
344 write_mmfm_byte(buffer , 0);
345 }
346 }
347
fill_with_gap3(std::vector<uint32_t> & buffer)348 void hpi_format::fill_with_gap3(std::vector<uint32_t> &buffer)
349 {
350 // Cell count in a track (1 µs cells in a 1/6 s track)
351 unsigned cell_count = (500000 * 120) / 360;
352 unsigned cells_in_buffer = buffer.size();
353 // Size of gap 3
354 unsigned gap_3 = (cell_count - cells_in_buffer) / 16;
355 // Gap 3
356 for (unsigned i = 0; i < gap_3; i++) {
357 write_mmfm_byte(buffer , 0);
358 }
359 // Last cell to round everything up to 2E+8
360 if (buffer.size() * CELL_SIZE < 200000000) {
361 bit_w(buffer , false , 200000000 - buffer.size() * CELL_SIZE);
362 }
363 }
364
chs_to_lba(unsigned cylinder,unsigned head,unsigned sector,unsigned heads)365 unsigned hpi_format::chs_to_lba(unsigned cylinder , unsigned head , unsigned sector , unsigned heads)
366 {
367 return sector + (head + cylinder * heads) * HPI_SECTORS;
368 }
369
get_next_id_n_block(const uint8_t * bitstream,int bitstream_size,int & pos,int & start_pos)370 std::vector<uint8_t> hpi_format::get_next_id_n_block(const uint8_t *bitstream , int bitstream_size , int& pos , int& start_pos)
371 {
372 std::vector<uint8_t> res;
373 uint32_t sr = 0;
374 // Look for either sync + ID AM or sync + DATA AM
375 while (pos < bitstream_size && sr != ID_CD_PATTERN && sr != DATA_CD_PATTERN) {
376 bool bit = util::BIT(bitstream[ pos >> 3 ] , 7 - (pos & 7));
377 pos++;
378 sr = (sr << 1) | bit;
379 }
380 if (pos == bitstream_size) {
381 // End of track reached
382 return res;
383 }
384 start_pos = pos;
385 // ID blocks: Track no. + sector/head no. + CRC
386 // Data blocks: Sector data + CRC
387 unsigned to_dump;
388 if (sr == ID_CD_PATTERN) {
389 to_dump = 4;
390 res.push_back(ID_AM);
391 } else {
392 to_dump = HPI_SECTOR_SIZE + 2;
393 res.push_back(DATA_AM);
394 }
395 // Align to data cells
396 pos++;
397 for (unsigned i = 0; i < to_dump && pos < bitstream_size; i++) {
398 uint8_t byte = 0;
399 unsigned j;
400 for (j = 0; j < 8 && pos < bitstream_size; j++) {
401 bool bit = util::BIT(bitstream[ pos >> 3 ] , 7 - (pos & 7));
402 pos += 2;
403 byte >>= 1;
404 if (bit) {
405 byte |= 0x80;
406 }
407 }
408 if (j == 8) {
409 res.push_back(byte);
410 }
411 }
412 return res;
413 }
414
get_next_sector(const uint8_t * bitstream,int bitstream_size,int & pos,unsigned & track,unsigned & head,unsigned & sector,uint8_t * sector_data)415 bool hpi_format::get_next_sector(const uint8_t *bitstream , int bitstream_size , int& pos , unsigned& track , unsigned& head , unsigned& sector , uint8_t *sector_data)
416 {
417 std::vector<uint8_t> block;
418 while (true) {
419 // Scan for ID block first
420 int id_pos = 0;
421 while (true) {
422 if (block.size() == 0) {
423 block = get_next_id_n_block(bitstream , bitstream_size , pos , id_pos);
424 if (block.size() == 0) {
425 return false;
426 }
427 }
428 if (block[ 0 ] == ID_AM && block.size() >= 3) {
429 track = block[ 1 ];
430 head = block[ 2 ] >> 7;
431 sector = block[ 2 ] & 0x7f;
432 break;
433 } else {
434 block.clear();
435 }
436 }
437 // Then for DATA block
438 int data_pos = 0;
439 block = get_next_id_n_block(bitstream , bitstream_size , pos , data_pos);
440 if (block.size() == 0) {
441 return false;
442 }
443 if (block[ 0 ] == DATA_AM && block.size() >= (HPI_SECTOR_SIZE + 1) && abs((data_pos - id_pos) - ID_DATA_OFFSET) <= 16) {
444 break;
445 }
446 }
447
448 for (unsigned i = 0; i < HPI_SECTOR_SIZE; i += 2) {
449 sector_data[ i ] = block[ i + 2 ];
450 sector_data[ i + 1 ] = block[ i + 1 ];
451 }
452
453 return true;
454 }
455
456 // This table comes straight from hp9895 firmware (it's @ 0x0f90)
457 // For each interleave factor in [1..29] it stores the number of positions
458 // to move forward in the interleaved sector list when beginning a new track.
459 // There are different offsets for tracks on head 0 and tracks on head 1.
460 const uint8_t hpi_format::m_track_skew[ HPI_SECTORS - 1 ][ HPI_HEADS ] = {
461 { 0x1c , 0x18 }, // Interleave = 1
462 { 0x1c , 0x18 }, // Interleave = 2
463 { 0x1c , 0x18 }, // Interleave = 3
464 { 0x1d , 0x1a }, // Interleave = 4
465 { 0x1a , 0x18 }, // Interleave = 5
466 { 0x19 , 0x18 }, // Interleave = 6
467 { 0x00 , 0x00 }, // Interleave = 7
468 { 0x1d , 0x1d }, // Interleave = 8
469 { 0x1c , 0x1c }, // Interleave = 9
470 { 0x15 , 0x15 }, // Interleave = 10
471 { 0x00 , 0x00 }, // Interleave = 11
472 { 0x19 , 0x19 }, // Interleave = 12
473 { 0x00 , 0x00 }, // Interleave = 13
474 { 0x1d , 0x1d }, // Interleave = 14
475 { 0x10 , 0x10 }, // Interleave = 15
476 { 0x1d , 0x1d }, // Interleave = 16
477 { 0x00 , 0x00 }, // Interleave = 17
478 { 0x19 , 0x19 }, // Interleave = 18
479 { 0x00 , 0x00 }, // Interleave = 19
480 { 0x15 , 0x15 }, // Interleave = 20
481 { 0x1c , 0x1c }, // Interleave = 21
482 { 0x1d , 0x1d }, // Interleave = 22
483 { 0x00 , 0x00 }, // Interleave = 23
484 { 0x19 , 0x19 }, // Interleave = 24
485 { 0x1a , 0x1a }, // Interleave = 25
486 { 0x1d , 0x1d }, // Interleave = 26
487 { 0x1c , 0x1c }, // Interleave = 27
488 { 0x1d , 0x1d }, // Interleave = 28
489 { 0x00 , 0x00 } // Interleave = 29
490 };
491
492 const floppy_format_type FLOPPY_HPI_FORMAT = &floppy_image_format_creator<hpi_format>;
493