1 // license:BSD-3-Clause
2 // copyright-holders: F. Ulivi
3 /*********************************************************************
4
5 img_dsk.cpp
6
7 "IMG" disk format
8
9 This format is just a raw image of every sector on SSDD 8" floppy
10 disks as used in Intel MDS-II systems.
11 Files with this format have no header/trailer and are exactly
12 512512 bytes in size (52 sectors, 1 head, 77 tracks,
13 128 bytes per sector).
14
15 *********************************************************************/
16
17 #include "img_dsk.h"
18
19 #include "coretmpl.h" // BIT
20
21
22 // Debugging
23 #define VERBOSE 0
24 #define LOG(...) do { if (VERBOSE) osd_printf_info(__VA_ARGS__); } while (false)
25
26 constexpr unsigned CELL_SIZE = 1200; // Bit cell size (1 µs)
27 constexpr uint8_t INDEX_AM = 0x0c; // Index address mark
28 constexpr uint8_t ID_AM = 0x0e; // ID address mark
29 constexpr uint8_t DATA_AM = 0x0b; // Data address mark
30 constexpr uint8_t AM_CLOCK = 0x70; // Clock pattern of AM
31 constexpr unsigned PREIDX_GAP = 45; // Size of pre-index gap
32 constexpr unsigned SYNC_00_LEN = 18; // 00's in sync (gaps 1, 2, 3)
33 constexpr unsigned SYNC_FF_LEN = 10; // FF's in sync (gaps 1, 2, 3)
34 constexpr int ID_DATA_OFFSET = 35 * 16; // Nominal distance (in cells) between ID & DATA AM
35 // Size of image file
36 constexpr unsigned IMG_IMAGE_SIZE = img_format::TRACKS * img_format::HEADS * img_format::SECTORS * img_format::SECTOR_SIZE;
37 constexpr uint16_t CRC_POLY = 0x1021; // CRC-CCITT
38
img_format()39 img_format::img_format()
40 {
41 }
42
identify(io_generic * io,uint32_t form_factor)43 int img_format::identify(io_generic *io, uint32_t form_factor)
44 {
45 uint64_t size = io_generic_size(io);
46
47 if (((form_factor == floppy_image::FF_8) || (form_factor == floppy_image::FF_UNKNOWN)) &&
48 size == IMG_IMAGE_SIZE) {
49 return 50;
50 } else {
51 return 0;
52 }
53 }
54
load(io_generic * io,uint32_t form_factor,floppy_image * image)55 bool img_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
56 {
57 uint64_t size = io_generic_size(io);
58 if (size != IMG_IMAGE_SIZE) {
59 return false;
60 }
61 image->set_variant(floppy_image::SSDD);
62
63 // Suck in the whole image
64 std::vector<uint8_t> image_data(size);
65 io_generic_read(io, (void *)image_data.data(), 0, size);
66
67 for (unsigned cyl = 0; cyl < TRACKS; cyl++) {
68 std::vector<uint32_t> track_data;
69
70 write_gap(track_data, 0 , PREIDX_GAP);
71 write_mmfm_byte(track_data , INDEX_AM , AM_CLOCK);
72
73 // Compute interleave factor and skew for current track
74 unsigned il_factor;
75 unsigned skew;
76 if (cyl == 0) {
77 il_factor = 1;
78 skew = 51;
79 } else if (cyl == 1) {
80 il_factor = 4;
81 skew = 48;
82 } else {
83 il_factor = 5;
84 skew = (47 + 45 * (cyl - 2)) % 52;
85 }
86 std::vector<uint8_t> sector_list = interleaved_sectors(il_factor);
87
88 for (unsigned sector = 0; sector < SECTORS; sector++) {
89 unsigned real_sector = sector_list[ (sector + skew) % SECTORS ];
90 unsigned offset_in_image = (real_sector - 1 + cyl * SECTORS) * SECTOR_SIZE;
91 write_sector(track_data , cyl , real_sector , &image_data[ offset_in_image ]);
92 }
93 fill_with_gap4(track_data);
94 generate_track_from_levels(cyl , 0 , track_data , 0 , image);
95 }
96 return true;
97 }
98
save(io_generic * io,floppy_image * image)99 bool img_format::save(io_generic *io, floppy_image *image)
100 {
101 for (int cyl = 0; cyl < TRACKS; cyl++) {
102 uint8_t bitstream[ 21000 ];
103 int bitstream_size;
104 generate_bitstream_from_track(cyl , 0 , CELL_SIZE , bitstream , bitstream_size , image , 0);
105 int pos = 0;
106 unsigned track_no , sector_no;
107 uint8_t sector_data[ SECTOR_SIZE ];
108 while (get_next_sector(bitstream , bitstream_size , pos , track_no , sector_no , sector_data)) {
109 if (track_no == cyl && sector_no >= 1 && sector_no <= SECTORS) {
110 unsigned offset_in_image = (cyl * SECTORS + sector_no - 1) * SECTOR_SIZE;
111 io_generic_write(io, sector_data, offset_in_image, SECTOR_SIZE);
112 }
113 }
114 }
115 return true;
116 }
117
name() const118 const char *img_format::name() const
119 {
120 return "img";
121 }
122
description() const123 const char *img_format::description() const
124 {
125 return "MDS-II floppy disk image";
126 }
127
extensions() const128 const char *img_format::extensions() const
129 {
130 return "img";
131 }
132
supports_save() const133 bool img_format::supports_save() const
134 {
135 return true;
136 }
137
interleaved_sectors(unsigned il_factor)138 std::vector<uint8_t> img_format::interleaved_sectors(unsigned il_factor)
139 {
140 std::vector<uint8_t> out(SECTORS);
141 unsigned idx = 0;
142 for (unsigned s = 0; s < SECTORS; s++) {
143 while (out[ idx ] != 0) {
144 if (++idx >= SECTORS) {
145 idx = 0;
146 }
147 }
148 out[ idx ] = s + 1;
149 idx += il_factor;
150 if (idx >= SECTORS) {
151 idx -= SECTORS;
152 }
153 }
154 return out;
155 }
156
write_mmfm_bit(std::vector<uint32_t> & buffer,bool data_bit,bool clock_bit)157 void img_format::write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit)
158 {
159 bool had_transition = buffer.size() < 2 ? false : bit_r(buffer, buffer.size() - 1) || bit_r(buffer , buffer.size() - 2);
160 clock_bit = !data_bit && (clock_bit || !had_transition);
161 bit_w(buffer , clock_bit , CELL_SIZE);
162 bit_w(buffer , data_bit , CELL_SIZE);
163
164 if (util::BIT(m_crc , 15) ^ data_bit) {
165 m_crc = (m_crc << 1) ^ CRC_POLY;
166 } else {
167 m_crc <<= 1;
168 }
169 }
170
write_mmfm_byte(std::vector<uint32_t> & buffer,uint8_t data,uint8_t clock)171 void img_format::write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint8_t clock)
172 {
173 for (int i = 7; i >= 0; i--) {
174 write_mmfm_bit(buffer , util::BIT(data , i) , util::BIT(clock , i));
175 }
176 }
177
write_sync(std::vector<uint32_t> & buffer)178 void img_format::write_sync(std::vector<uint32_t> &buffer)
179 {
180 write_gap(buffer , SYNC_00_LEN , SYNC_FF_LEN);
181 }
182
write_crc(std::vector<uint32_t> & buffer,uint16_t crc)183 void img_format::write_crc(std::vector<uint32_t> &buffer , uint16_t crc)
184 {
185 // Note that CRC is stored with MSB (x^15) first
186 for (unsigned i = 0; i < 16; i++) {
187 write_mmfm_bit(buffer , util::BIT(crc , 15 - i) , 0);
188 }
189 }
190
write_gap(std::vector<uint32_t> & buffer,unsigned size_00,unsigned size_ff)191 void img_format::write_gap(std::vector<uint32_t> &buffer , unsigned size_00 , unsigned size_ff)
192 {
193 for (unsigned i = 0; i < size_00; ++i) {
194 write_mmfm_byte(buffer, 0);
195 }
196 for (unsigned i = 0; i < size_ff; ++i) {
197 write_mmfm_byte(buffer, 0xff);
198 }
199 }
200
write_sector(std::vector<uint32_t> & buffer,uint8_t track_no,uint8_t sect_no,const uint8_t * sect_data)201 void img_format::write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_no , const uint8_t *sect_data)
202 {
203 // **** On-disk format of a sector ****
204 //
205 // | Offset | Size | Value | Content |
206 // |--------+------+-------+----------------------|
207 // | 0 | 18 | 00 | Gap 1/3 |
208 // | 18 | 10 | ff | Gap 1/3 |
209 // | 28 | 1 | 0e | ID AM (clock = 70) |
210 // | 29 | 1 | xx | Track no. |
211 // | 30 | 1 | 00 | N/U |
212 // | 31 | 1 | xx | Sector no. |
213 // | 32 | 1 | 00 | N/U |
214 // | 33 | 2 | xx | ID CRC |
215 // | 35 | 18 | 00 | Gap 2 |
216 // | 53 | 10 | ff | Gap 2 |
217 // | 63 | 1 | 0b | Data AM (clock = 70) |
218 // | 64 | 128 | xx | Sector data |
219 // | 192 | 2 | xx | Data CRC |
220 // | 194 | | | |
221
222 // Gap1
223 write_sync(buffer);
224 // ID AM
225 m_crc = 0;
226 write_mmfm_byte(buffer , ID_AM , AM_CLOCK);
227 // Track #
228 write_mmfm_byte(buffer , track_no);
229 write_mmfm_byte(buffer , 0);
230 // Sector #
231 write_mmfm_byte(buffer , sect_no);
232 write_mmfm_byte(buffer , 0);
233 // ID CRC
234 write_crc(buffer , m_crc);
235 // Gap 2
236 write_sync(buffer);
237 // Data AM
238 m_crc = 0;
239 write_mmfm_byte(buffer , DATA_AM , AM_CLOCK);
240 for (unsigned i = 0; i < SECTOR_SIZE; i++) {
241 // Data
242 write_mmfm_byte(buffer , sect_data[ i ]);
243 }
244 // Data CRC
245 write_crc(buffer , m_crc);
246 }
247
fill_with_gap4(std::vector<uint32_t> & buffer)248 void img_format::fill_with_gap4(std::vector<uint32_t> &buffer)
249 {
250 // Cell count in a track (1 µs cells in a 1/6 s track)
251 unsigned cell_count = (500000 * 120) / 360;
252 unsigned cells_in_buffer = buffer.size();
253 // Size of gap 4
254 unsigned gap_4 = (cell_count - cells_in_buffer) / 16;
255 // Gap 4
256 write_gap(buffer , gap_4 , 0);
257 // Last cell to round everything up to 2E+8
258 if (buffer.size() * CELL_SIZE < 200000000) {
259 bit_w(buffer , false , 200000000 - buffer.size() * CELL_SIZE);
260 }
261 }
262
get_next_id_n_block(const uint8_t * bitstream,int bitstream_size,int & pos,int & start_pos)263 std::vector<uint8_t> img_format::get_next_id_n_block(const uint8_t *bitstream , int bitstream_size , int& pos , int& start_pos)
264 {
265 std::vector<uint8_t> res;
266 uint8_t data_sr;
267 uint8_t clock_sr;
268
269 // Look for either sync + ID AM or sync + DATA AM
270 do {
271 unsigned cnt_trans = 0;
272 while (pos < bitstream_size && cnt_trans < 34) {
273 bool bit = util::BIT(bitstream[ pos >> 3 ] , ~pos & 7);
274 pos++;
275 if (cnt_trans < 32) {
276 if (!(util::BIT(cnt_trans , 0) ^ bit)) {
277 cnt_trans++;
278 } else {
279 cnt_trans = 0;
280 }
281 } else if (cnt_trans == 32) {
282 if (!bit) {
283 start_pos = pos;
284 cnt_trans++;
285 } else {
286 cnt_trans = 0;
287 }
288 } else if (!bit) {
289 cnt_trans++;
290 } else {
291 cnt_trans = 32;
292 }
293 }
294
295 if (pos == bitstream_size) {
296 // End of track reached
297 return res;
298 }
299
300 // Get AM
301 data_sr = clock_sr = 0;
302 for (unsigned i = 0; i < 7; ++i) {
303 bool bit = util::BIT(bitstream[ pos >> 3 ] , ~pos & 7);
304 pos++;
305 clock_sr = (clock_sr << 1) | bit;
306 bit = util::BIT(bitstream[ pos >> 3 ] , ~pos & 7);
307 pos++;
308 data_sr = (data_sr << 1) | bit;
309 }
310 if (pos >= bitstream_size) {
311 return res;
312 }
313 } while (clock_sr != AM_CLOCK);
314
315 // ID blocks: Track no. + 0 + sector no. + 0 + CRC
316 // Data blocks: Sector data + CRC
317 res.push_back(data_sr);
318 unsigned to_dump;
319 if (data_sr == ID_AM) {
320 to_dump = 4;
321 } else if (data_sr == DATA_AM) {
322 to_dump = SECTOR_SIZE;
323 } else {
324 to_dump = 0;
325 }
326
327 pos++;
328 for (unsigned i = 0; i < to_dump && pos < bitstream_size; i++) {
329 data_sr = 0;
330 unsigned j;
331 for (j = 0; j < 8 && pos < bitstream_size; j++) {
332 bool bit = util::BIT(bitstream[ pos >> 3 ] , ~pos & 7);
333 pos += 2;
334 data_sr = (data_sr << 1) | bit;
335 }
336 if (j == 8) {
337 res.push_back(data_sr);
338 }
339 }
340 return res;
341 }
342
get_next_sector(const uint8_t * bitstream,int bitstream_size,int & pos,unsigned & track,unsigned & sector,uint8_t * sector_data)343 bool img_format::get_next_sector(const uint8_t *bitstream , int bitstream_size , int& pos , unsigned& track , unsigned& sector , uint8_t *sector_data)
344 {
345 std::vector<uint8_t> block;
346 while (true) {
347 // Scan for ID block first
348 int id_pos = 0;
349 while (true) {
350 if (block.size() == 0) {
351 block = get_next_id_n_block(bitstream , bitstream_size , pos , id_pos);
352 if (block.size() == 0) {
353 return false;
354 }
355 }
356 if (block[ 0 ] == ID_AM && block.size() >= 5) {
357 track = block[ 1 ];
358 sector = block[ 3 ];
359 break;
360 } else {
361 block.clear();
362 }
363 }
364 // Then for DATA block
365 int data_pos = 0;
366 block = get_next_id_n_block(bitstream , bitstream_size , pos , data_pos);
367 if (block.size() == 0) {
368 return false;
369 }
370 if (block[ 0 ] == DATA_AM && block.size() >= (SECTOR_SIZE + 1) && abs((data_pos - id_pos) - ID_DATA_OFFSET) <= 64) {
371 break;
372 }
373 }
374
375 memcpy(sector_data , block.data() + 1 , SECTOR_SIZE);
376
377 return true;
378 }
379
380 const floppy_format_type FLOPPY_IMG_FORMAT = &floppy_image_format_creator<img_format>;
381