void mfm_harddisk_device::call_unload() { mfmhd_layout_params* params = m_format->get_current_params(); mfmhd_layout_params* oldparams = m_format->get_initial_params(); if (m_cache!=nullptr) { m_cache->cleanup(); if (m_format->save_param(MFMHD_IL) && !params->equals_rec(oldparams)) { logerror("MFM HD sector arrangement and recording specs have changed; updating CHD metadata\n"); chd_file* chdfile = get_chd_file(); chd_error err = chdfile->write_metadata(MFM_HARD_DISK_METADATA_TAG, 0, string_format(MFMHD_REC_METADATA_FORMAT, params->interleave, params->cylskew, params->headskew, params->write_precomp_cylinder, params->reduced_wcurr_cylinder), 0); if (err != CHDERR_NONE) { logerror("Failed to save MFM HD sector arrangement/recording specs to CHD\n"); } } if (m_format->save_param(MFMHD_GAP1) && !params->equals_gap(oldparams)) { logerror("MFM HD track gap specs have changed; updating CHD metadata\n"); chd_file* chdfile = get_chd_file(); chd_error err = chdfile->write_metadata(MFM_HARD_DISK_METADATA_TAG, 1, string_format(MFMHD_GAP_METADATA_FORMAT, params->gap1, params->gap2, params->gap3, params->sync, params->headerlen, params->ecctype), 0); if (err != CHDERR_NONE) { logerror("Failed to save MFM HD track gap specs to CHD\n"); } } } harddisk_image_device::call_unload(); }
/* Load the image from the CHD. We also calculate the head timing here because we need the number of cylinders, and for generic drives we get them from the CHD. */ bool mfm_harddisk_device::call_load() { bool loaded = harddisk_image_device::call_load(); std::string devtag(tag()); devtag += ":format"; m_format->set_tag(devtag); if (loaded==IMAGE_INIT_PASS) { std::string metadata; chd_file* chdfile = get_chd_file(); if (chdfile==NULL) { logerror("%s: chdfile is null\n", tag()); return IMAGE_INIT_FAIL; } // Read the hard disk metadata chd_error state = chdfile->read_metadata(HARD_DISK_METADATA_TAG, 0, metadata); if (state != CHDERR_NONE) { logerror("%s: Failed to read CHD metadata\n", tag()); return IMAGE_INIT_FAIL; } if (TRACE_CONFIG) logerror("%s: CHD metadata: %s\n", tag(), metadata.c_str()); // Parse the metadata mfmhd_layout_params param; param.encoding = m_encoding; if (TRACE_CONFIG) logerror("%s: Set encoding to %d\n", tag(), m_encoding); if (sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, ¶m.cylinders, ¶m.heads, ¶m.sectors_per_track, ¶m.sector_size) != 4) { logerror("%s: Invalid CHD metadata\n", tag()); return IMAGE_INIT_FAIL; } if (TRACE_CONFIG) logerror("%s: CHD image has geometry cyl=%d, head=%d, sect=%d, size=%d\n", tag(), param.cylinders, param.heads, param.sectors_per_track, param.sector_size); if (m_max_cylinders != 0 && (param.cylinders != m_max_cylinders || param.heads != m_max_heads)) { throw emu_fatalerror("Image geometry does not fit this kind of hard drive: drive=(%d,%d), image=(%d,%d)", m_max_cylinders, m_max_heads, param.cylinders, param.heads); } // MDM format specs param.interleave = 0; param.cylskew = 0; param.headskew = 0; param.write_precomp_cylinder = -1; param.reduced_wcurr_cylinder = -1; state = chdfile->read_metadata(MFM_HARD_DISK_METADATA_TAG, 0, metadata); if (state != CHDERR_NONE) { logerror("%s: Failed to read CHD sector arrangement/recording specs, applying defaults\n", tag()); } else { sscanf(metadata.c_str(), MFMHD_REC_METADATA_FORMAT, ¶m.interleave, ¶m.cylskew, ¶m.headskew, ¶m.write_precomp_cylinder, ¶m.reduced_wcurr_cylinder); } if (!param.sane_rec()) { if (TRACE_CONFIG) logerror("%s: Sector arrangement/recording specs have invalid values, applying defaults\n", tag()); param.reset_rec(); } else if (TRACE_CONFIG) logerror("%s: MFM HD rec specs: interleave=%d, cylskew=%d, headskew=%d, wpcom=%d, rwc=%d\n", tag(), param.interleave, param.cylskew, param.headskew, param.write_precomp_cylinder, param.reduced_wcurr_cylinder); state = chdfile->read_metadata(MFM_HARD_DISK_METADATA_TAG, 1, metadata); if (state != CHDERR_NONE) { logerror("%s: Failed to read CHD track gap specs, applying defaults\n", tag()); } else { sscanf(metadata.c_str(), MFMHD_GAP_METADATA_FORMAT, ¶m.gap1, ¶m.gap2, ¶m.gap3, ¶m.sync, ¶m.headerlen, ¶m.ecctype); } if (!param.sane_gap()) { if (TRACE_CONFIG) logerror("%s: MFM HD gap specs have invalid values, applying defaults\n", tag()); param.reset_gap(); } else if (TRACE_CONFIG) logerror("%s: MFM HD gap specs: gap1=%d, gap2=%d, gap3=%d, sync=%d, headerlen=%d, ecctype=%d\n", tag(), param.gap1, param.gap2, param.gap3, param.sync, param.headerlen, param.ecctype); m_format->set_layout_params(param); m_cache->init(this, m_trackimage_size, m_cachelines); // Head timing // We assume that the real times are 80% of the max times // The single-step time includes the settle time, so does the max time // From that we calculate the actual cylinder-by-cylinder time and the settle time m_actual_cylinders = param.cylinders; if (m_phys_cylinders == 0) m_phys_cylinders = m_actual_cylinders+1; if (m_landing_zone == 0) m_landing_zone = m_phys_cylinders-1; float realnext = (m_seeknext_time==0)? 10 : (m_seeknext_time * 0.8); float realmax = (m_maxseek_time==0)? (m_actual_cylinders * 0.2) : (m_maxseek_time * 0.8); float settle_us = ((m_actual_cylinders-1.0) * realnext - realmax) / (m_actual_cylinders-2.0) * 1000; float step_us = realnext * 1000 - settle_us; if (TRACE_CONFIG) logerror("%s: Calculated settle time: %0.2f ms, step: %d us\n", tag(), settle_us/1000, (int)step_us); m_settle_time = attotime::from_usec((int)settle_us); m_step_time = attotime::from_usec((int)step_us); m_current_cylinder = m_landing_zone; } else { logerror("%s: Could not load CHD\n", tag()); } return loaded; }