trunk/src/lib/formats/wd177x_dsk.c
| r18859 | r18860 | |
| 1 | /*************************************************************************** |
| 2 | |
| 3 | Copyright Olivier Galibert |
| 4 | All rights reserved. |
| 5 | |
| 6 | Redistribution and use in source and binary forms, with or without |
| 7 | modification, are permitted provided that the following conditions are |
| 8 | met: |
| 9 | |
| 10 | * Redistributions of source code must retain the above copyright |
| 11 | notice, this list of conditions and the following disclaimer. |
| 12 | * Redistributions in binary form must reproduce the above copyright |
| 13 | notice, this list of conditions and the following disclaimer in |
| 14 | the documentation and/or other materials provided with the |
| 15 | distribution. |
| 16 | * Neither the name 'MAME' nor the names of its contributors may be |
| 17 | used to endorse or promote products derived from this software |
| 18 | without specific prior written permission. |
| 19 | |
| 20 | THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR |
| 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 23 | DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, |
| 24 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 27 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| 28 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
| 29 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 30 | POSSIBILITY OF SUCH DAMAGE. |
| 31 | |
| 32 | ****************************************************************************/ |
| 33 | |
| 34 | /********************************************************************* |
| 35 | |
| 36 | formats/wd177x_dsk.h |
| 37 | |
| 38 | helper for simple wd177x-formatted disk images |
| 39 | |
| 40 | *********************************************************************/ |
| 41 | |
| 42 | #include "emu.h" |
| 43 | #include "formats/wd177x_dsk.h" |
| 44 | |
| 45 | wd177x_format::wd177x_format(const format *_formats) |
| 46 | { |
| 47 | formats = _formats; |
| 48 | } |
| 49 | |
| 50 | int wd177x_format::find_size(io_generic *io, UINT32 form_factor) |
| 51 | { |
| 52 | int size = io_generic_size(io); |
| 53 | for(int i=0; formats[i].form_factor; i++) { |
| 54 | const format &f = formats[i]; |
| 55 | if(form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor) |
| 56 | continue; |
| 57 | |
| 58 | if(size == compute_track_size(f) * f.track_count * f.head_count) |
| 59 | return i; |
| 60 | } |
| 61 | return -1; |
| 62 | } |
| 63 | |
| 64 | int wd177x_format::identify(io_generic *io, UINT32 form_factor) |
| 65 | { |
| 66 | int type = find_size(io, form_factor); |
| 67 | |
| 68 | if(type != -1) |
| 69 | return 50; |
| 70 | return 0; |
| 71 | } |
| 72 | |
| 73 | int wd177x_format::compute_track_size(const format &f) const |
| 74 | { |
| 75 | int track_size; |
| 76 | if(f.sector_base_size) |
| 77 | track_size = f.sector_base_size * f.sector_count; |
| 78 | else { |
| 79 | track_size = 0; |
| 80 | for(int i=0; i != f.sector_count; i++) |
| 81 | track_size += f.per_sector_size[i]; |
| 82 | } |
| 83 | return track_size; |
| 84 | } |
| 85 | |
| 86 | void wd177x_format::build_sector_description(const format &f, UINT8 *sectdata, desc_s *sectors) const |
| 87 | { |
| 88 | if(f.sector_base_id == -1) { |
| 89 | for(int i=0; i<f.sector_count; i++) { |
| 90 | int cur_offset = 0; |
| 91 | for(int j=0; j<f.sector_count; j++) |
| 92 | if(f.per_sector_id[j] < f.per_sector_id[i]) |
| 93 | cur_offset += f.sector_base_size ? f.sector_base_size : f.per_sector_size[j]; |
| 94 | sectors[i].data = sectdata + cur_offset; |
| 95 | sectors[i].size = f.sector_base_size ? f.sector_base_size : f.per_sector_size[i]; |
| 96 | sectors[i].sector_id = f.per_sector_id[i]; |
| 97 | } |
| 98 | } else { |
| 99 | int cur_offset = 0; |
| 100 | for(int i=0; i<f.sector_count; i++) { |
| 101 | sectors[i].data = sectdata + cur_offset; |
| 102 | sectors[i].size = f.sector_base_size ? f.sector_base_size : f.per_sector_size[i]; |
| 103 | cur_offset += sectors[i].size; |
| 104 | sectors[i].sector_id = i + f.sector_base_id; |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | bool wd177x_format::load(io_generic *io, UINT32 form_factor, floppy_image *image) |
| 110 | { |
| 111 | int type = find_size(io, form_factor); |
| 112 | if(type == -1) |
| 113 | return false; |
| 114 | |
| 115 | const format &f = formats[type]; |
| 116 | |
| 117 | floppy_image_format_t::desc_e desc[] = { |
| 118 | /* 00 */ { MFM, 0x4e, f.gap_1 }, |
| 119 | /* 01 */ { SECTOR_LOOP_START, 0, f.sector_count-1 }, |
| 120 | /* 02 */ { MFM, 0x00, 12 }, |
| 121 | /* 03 */ { CRC_CCITT_START, 1 }, |
| 122 | /* 04 */ { RAW, 0x4489, 3 }, |
| 123 | /* 05 */ { MFM, 0xfe, 1 }, |
| 124 | /* 06 */ { TRACK_ID }, |
| 125 | /* 07 */ { HEAD_ID }, |
| 126 | /* 08 */ { SECTOR_ID }, |
| 127 | /* 09 */ { SIZE_ID }, |
| 128 | /* 10 */ { CRC_END, 1 }, |
| 129 | /* 11 */ { CRC, 1 }, |
| 130 | /* 12 */ { MFM, 0x4e, f.gap_2 }, |
| 131 | /* 13 */ { MFM, 0x00, 12 }, |
| 132 | /* 14 */ { CRC_CCITT_START, 2 }, |
| 133 | /* 15 */ { RAW, 0x4489, 3 }, |
| 134 | /* 16 */ { MFM, 0xfb, 1 }, |
| 135 | /* 17 */ { SECTOR_DATA, -1 }, |
| 136 | /* 18 */ { CRC_END, 2 }, |
| 137 | /* 19 */ { CRC, 2 }, |
| 138 | /* 20 */ { MFM, 0x4e, f.gap_3 }, |
| 139 | /* 21 */ { SECTOR_LOOP_END }, |
| 140 | /* 22 */ { MFM, 0x4e, 0 }, |
| 141 | /* 23 */ { RAWBITS, 0x9254, 0 }, |
| 142 | /* 24 */ { END } |
| 143 | }; |
| 144 | |
| 145 | int current_size = f.gap_1*16; |
| 146 | if(f.sector_base_size) |
| 147 | current_size += f.sector_base_size * f.sector_count * 16; |
| 148 | else { |
| 149 | for(int j=0; j != f.sector_count; j++) |
| 150 | current_size += f.per_sector_size[j] * 16; |
| 151 | } |
| 152 | current_size += (12+3+1+4+2+f.gap_2+12+3+1+2+f.gap_3) * f.sector_count * 16; |
| 153 | |
| 154 | int total_size = 200000000/f.cell_size; |
| 155 | int remaining_size = total_size - current_size; |
| 156 | if(remaining_size < 0) |
| 157 | throw emu_fatalerror("wd177x_format: Incorrect track layout, max_size=%d, current_size=%d", total_size, current_size); |
| 158 | |
| 159 | // Fixup the end gap |
| 160 | desc[22].p2 = remaining_size / 16; |
| 161 | desc[23].p2 = remaining_size & 15; |
| 162 | desc[23].p1 >>= 16-(remaining_size & 15); |
| 163 | |
| 164 | int track_size = compute_track_size(f); |
| 165 | |
| 166 | UINT8 sectdata[40*512]; |
| 167 | desc_s sectors[40]; |
| 168 | build_sector_description(f, sectdata, sectors); |
| 169 | |
| 170 | for(int track=0; track < f.track_count; track++) |
| 171 | for(int head=0; head < f.head_count; head++) { |
| 172 | io_generic_read(io, sectdata, (track*f.head_count + head)*track_size, track_size); |
| 173 | generate_track(desc, track, head, sectors, f.sector_count, total_size, image); |
| 174 | } |
| 175 | |
| 176 | image->set_variant(f.variant); |
| 177 | |
| 178 | return true; |
| 179 | } |
| 180 | |
| 181 | bool wd177x_format::supports_save() const |
| 182 | { |
| 183 | return true; |
| 184 | } |
| 185 | |
| 186 | bool wd177x_format::save(io_generic *io, floppy_image *image) |
| 187 | { |
| 188 | // Count the number of formats |
| 189 | int formats_count; |
| 190 | for(formats_count=0; formats[formats_count].form_factor; formats_count++); |
| 191 | |
| 192 | // Allocate the storage for the list of testable formats for a |
| 193 | // given cell size |
| 194 | int *candidates = global_alloc_array(int, formats_count); |
| 195 | |
| 196 | // Format we're finally choosing |
| 197 | int chosen_candidate = -1; |
| 198 | |
| 199 | // Previously tested cell size |
| 200 | int min_cell_size = 0; |
| 201 | for(;;) { |
| 202 | // Build the list of all formats for the immediatly superior cell size |
| 203 | int cur_cell_size = 0; |
| 204 | int candidates_count = 0; |
| 205 | for(int i=0; i != formats_count; i++) { |
| 206 | if(image->get_form_factor() == floppy_image::FF_UNKNOWN || |
| 207 | image->get_form_factor() == formats[i].form_factor) { |
| 208 | if(formats[i].cell_size == cur_cell_size) |
| 209 | candidates[candidates_count++] = i; |
| 210 | else if((!cur_cell_size || formats[i].cell_size < cur_cell_size) && |
| 211 | formats[i].cell_size > min_cell_size) { |
| 212 | candidates[0] = i; |
| 213 | candidates_count = 1; |
| 214 | cur_cell_size = formats[i].cell_size; |
| 215 | } |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | min_cell_size = cur_cell_size; |
| 220 | |
| 221 | // No candidates with a cell size bigger than the previously |
| 222 | // tested one, we're done |
| 223 | if(!candidates_count) |
| 224 | break; |
| 225 | |
| 226 | // Filter with track 0 head 0 |
| 227 | check_compatibility(image, candidates, candidates_count); |
| 228 | |
| 229 | // Nobody matches, try with the next cell size |
| 230 | if(!candidates_count) |
| 231 | continue; |
| 232 | |
| 233 | // We have a match at that cell size, we just need to find the |
| 234 | // best one given the geometry |
| 235 | |
| 236 | // If there's only one, we're done |
| 237 | if(candidates_count == 1) { |
| 238 | chosen_candidate = candidates[0]; |
| 239 | break; |
| 240 | } |
| 241 | |
| 242 | // Otherwise, find the best |
| 243 | int tracks, heads; |
| 244 | image->get_actual_geometry(tracks, heads); |
| 245 | chosen_candidate = candidates[0]; |
| 246 | for(int i=1; i != candidates_count; i++) { |
| 247 | const format &cc = formats[chosen_candidate]; |
| 248 | const format &cn = formats[candidates[i]]; |
| 249 | |
| 250 | // Handling enough sides is better than not |
| 251 | if(cn.head_count >= heads && cc.head_count < heads) |
| 252 | goto change; |
| 253 | else if(cc.head_count >= heads && cn.head_count < heads) |
| 254 | goto dont_change; |
| 255 | |
| 256 | // Since we're limited to two heads, at that point head |
| 257 | // count is identical for both formats. |
| 258 | |
| 259 | // Handling enough tracks is better than not |
| 260 | if(cn.track_count >= tracks && cc.track_count < tracks) |
| 261 | goto change; |
| 262 | else if(cn.track_count >= tracks && cc.track_count < tracks) |
| 263 | goto dont_change; |
| 264 | |
| 265 | // Both are on the same side of the track count, so closest is best |
| 266 | if(cc.track_count < tracks && cn.track_count > cc.track_count) |
| 267 | goto change; |
| 268 | if(cc.track_count >= tracks && cn.track_count < cc.track_count) |
| 269 | goto change; |
| 270 | goto dont_change; |
| 271 | |
| 272 | change: |
| 273 | chosen_candidate = candidates[i]; |
| 274 | dont_change: |
| 275 | ; |
| 276 | } |
| 277 | // We have a winner, bail out |
| 278 | break; |
| 279 | } |
| 280 | |
| 281 | // No match, pick the first one and be done with it |
| 282 | if(chosen_candidate == -1) |
| 283 | chosen_candidate = 0; |
| 284 | |
| 285 | |
| 286 | const format &f = formats[chosen_candidate]; |
| 287 | int track_size = compute_track_size(f); |
| 288 | |
| 289 | UINT8 sectdata[40*512]; |
| 290 | desc_s sectors[40]; |
| 291 | build_sector_description(f, sectdata, sectors); |
| 292 | |
| 293 | for(int track=0; track < f.track_count; track++) |
| 294 | for(int head=0; head < f.head_count; head++) { |
| 295 | extract_sectors(image, f, sectors, track, head); |
| 296 | io_generic_write(io, sectdata, (track*f.head_count + head)*track_size, track_size); |
| 297 | } |
| 298 | |
| 299 | return true; |
| 300 | } |
| 301 | |
| 302 | void wd177x_format::check_compatibility(floppy_image *image, int *candidates, int &candidates_count) |
| 303 | { |
| 304 | UINT8 bitstream[500000/8]; |
| 305 | UINT8 sectdata[50000]; |
| 306 | desc_xs sectors[256]; |
| 307 | int track_size; |
| 308 | |
| 309 | // Extract the sectors |
| 310 | generate_bitstream_from_track(0, 0, formats[candidates[0]].cell_size, bitstream, track_size, image); |
| 311 | extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata)); |
| 312 | |
| 313 | // Check compatibility with every candidate, copy in-place |
| 314 | int *ok_cands = candidates; |
| 315 | for(int i=0; i != candidates_count; i++) { |
| 316 | const format &f = formats[candidates[i]]; |
| 317 | int ns = 0; |
| 318 | for(int j=0; j<256; j++) |
| 319 | if(sectors[j].data) { |
| 320 | int sid; |
| 321 | if(f.sector_base_id == -1) { |
| 322 | for(sid=0; sid < f.sector_count; sid++) |
| 323 | if(f.per_sector_id[sid] == j) |
| 324 | break; |
| 325 | } else |
| 326 | sid = j - f.sector_base_id; |
| 327 | if(sid < 0 || sid > f.sector_count) |
| 328 | goto fail; |
| 329 | if(f.sector_base_size) { |
| 330 | if(sectors[j].size != f.sector_base_size) |
| 331 | goto fail; |
| 332 | } else { |
| 333 | if(sectors[j].size != f.per_sector_size[sid]) |
| 334 | goto fail; |
| 335 | } |
| 336 | ns++; |
| 337 | } |
| 338 | if(ns == f.sector_count) |
| 339 | *ok_cands++ = candidates[i]; |
| 340 | fail: |
| 341 | ; |
| 342 | } |
| 343 | candidates_count = ok_cands - candidates; |
| 344 | } |
| 345 | |
| 346 | |
| 347 | void wd177x_format::extract_sectors(floppy_image *image, const format &f, desc_s *sdesc, int track, int head) |
| 348 | { |
| 349 | UINT8 bitstream[500000/8]; |
| 350 | UINT8 sectdata[50000]; |
| 351 | desc_xs sectors[256]; |
| 352 | int track_size; |
| 353 | |
| 354 | // Extract the sectors |
| 355 | generate_bitstream_from_track(track, head, f.cell_size, bitstream, track_size, image); |
| 356 | extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata)); |
| 357 | |
| 358 | for(int i=0; i<f.sector_count; i++) { |
| 359 | desc_s &ds = sdesc[i]; |
| 360 | desc_xs &xs = sectors[ds.sector_id]; |
| 361 | if(!xs.data) |
| 362 | memset((void *)ds.data, 0, ds.size); |
| 363 | else if(xs.size < ds.size) { |
| 364 | memcpy((void *)ds.data, xs.data, xs.size); |
| 365 | memset((UINT8 *)ds.data + xs.size, 0, xs.size - ds.size); |
| 366 | } else |
| 367 | memcpy((void *)ds.data, xs.data, ds.size); |
| 368 | } |
| 369 | } |