// license:LGPL-2.1+ // copyright-holders:Michael Zapf /**************************************************************************** MFM hard disk emulation See mfmhd.c for documentation Michael Zapf August 2015 *****************************************************************************/ #ifndef MAME_DEVICES_IMAGEDEV_MFMHD_H #define MAME_DEVICES_IMAGEDEV_MFMHD_H #pragma once #include "imagedev/harddriv.h" #include "formats/mfm_hd.h" class mfm_harddisk_device; class mfmhd_trackimage { public: bool dirty; int cylinder; int head; uint16_t* encdata; // MFM encoding per byte mfmhd_trackimage* next; }; class mfmhd_trackimage_cache { public: mfmhd_trackimage_cache(running_machine &machine); ~mfmhd_trackimage_cache(); void init(mfm_harddisk_device* mfmhd, int tracksize, int trackslots); uint16_t* get_trackimage(int cylinder, int head); void mark_current_as_dirty(); void cleanup(); void write_back_one(); private: mfm_harddisk_device* m_mfmhd; mfmhd_trackimage* m_tracks; running_machine & m_machine; }; class mfm_harddisk_device : public harddisk_image_device { public: ~mfm_harddisk_device(); typedef delegate index_pulse_cb; typedef delegate ready_cb; typedef delegate seek_complete_cb; void setup_index_pulse_cb(index_pulse_cb cb); void setup_ready_cb(ready_cb cb); void setup_seek_complete_cb(seek_complete_cb cb); // Configuration void set_encoding(mfmhd_enc_t encoding) { m_encoding = encoding; } void set_spinup_time(int spinupms) { m_spinupms = spinupms; } void set_cache_size(int tracks) { m_cachelines = tracks; } void set_format(mfmhd_image_format_t* format) { m_format = format; } mfmhd_enc_t get_encoding() { return m_encoding; } // Active low lines. We're using ASSERT=0 / CLEAR=1 line_state ready_r() { return m_ready? ASSERT_LINE : CLEAR_LINE; } line_state seek_complete_r() { return m_seek_complete? ASSERT_LINE : CLEAR_LINE; } ; line_state trk00_r() { return m_current_cylinder==0? ASSERT_LINE : CLEAR_LINE; } // Data output towards controller bool read(attotime &from_when, const attotime &limit, uint16_t &data); // Data input from controller bool write(attotime &from_when, const attotime &limit, uint16_t cdata, bool wpcom=false, bool reduced_wc=false); // Step void step_w(line_state line); void direction_in_w(line_state line); // Head select void headsel_w(int head) { m_current_head = head & 0x0f; } image_init_result call_load() override; void call_unload() override; // Tells us the time when the track ends (next index pulse). Needed by the controller. attotime track_end_time(); // Access the tracks on the image. Used as a callback from the cache. chd_error load_track(uint16_t* data, int cylinder, int head); void write_track(uint16_t* data, int cylinder, int head); // Delivers the number of heads according to the loaded image int get_actual_heads(); protected: mfm_harddisk_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); void device_start() override; void device_stop() override; void device_reset() override; void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; std::string tts(const attotime &t); emu_timer *m_index_timer, *m_spinup_timer, *m_seek_timer, *m_cache_timer; index_pulse_cb m_index_pulse_cb; ready_cb m_ready_cb; seek_complete_cb m_seek_complete_cb; int m_max_cylinders; int m_phys_cylinders; int m_actual_cylinders; // after reading the CHD int m_max_heads; int m_landing_zone; int m_precomp_cyl; int m_redwc_cyl; int m_maxseek_time; int m_seeknext_time; private: mfmhd_enc_t m_encoding; int m_cell_size; // nanoseconds int m_trackimage_size; // number of 16-bit cell blocks (data bytes) int m_spinupms; int m_rpm; int m_interleave; int m_cachelines; bool m_ready; int m_current_cylinder; int m_current_head; int m_track_delta; int m_step_phase; bool m_seek_complete; bool m_seek_inward; bool m_autotruncation; bool m_recalibrated; line_state m_step_line; // keep the last state attotime m_spinup_time; attotime m_revolution_start_time; attotime m_rev_time; attotime m_settle_time; attotime m_step_time; mfmhd_trackimage_cache* m_cache; mfmhd_image_format_t* m_format; void head_move(); void recalibrate(); // Common routine for read/write bool find_position(attotime &from_when, const attotime &limit, int &bytepos, int &bitpos); }; /* The Generic drive is a MFM drive that has just enough heads and cylinders to handle the CHD image. Specific Seagate models: ST-213: 10 MB ST-225: 20 MB ST-251: 40 MB */ class mfm_hd_generic_device : public mfm_harddisk_device { public: mfm_hd_generic_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); }; DECLARE_DEVICE_TYPE(MFMHD_GENERIC, mfm_hd_generic_device) class mfm_hd_st213_device : public mfm_harddisk_device { public: mfm_hd_st213_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); }; DECLARE_DEVICE_TYPE(MFMHD_ST213, mfm_hd_st213_device) class mfm_hd_st225_device : public mfm_harddisk_device { public: mfm_hd_st225_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); }; DECLARE_DEVICE_TYPE(MFMHD_ST225, mfm_hd_st225_device) class mfm_hd_st251_device : public mfm_harddisk_device { public: mfm_hd_st251_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); }; DECLARE_DEVICE_TYPE(MFMHD_ST251, mfm_hd_st251_device) /* Connector for a MFM hard disk. Similar concept as in floppy.cpp. */ class mfm_harddisk_connector : public device_t, public device_slot_interface { public: template mfm_harddisk_connector(const machine_config &mconfig, const char *tag, device_t *owner, T &&opts, const char *dflt, mfmhd_enc_t enc, int spinup, int cache, const mfmhd_format_type format, bool fixed = false) : mfm_harddisk_connector(mconfig, tag, owner, 0) { option_reset(); opts(*this); set_default_option(dflt); set_fixed(fixed); configure(enc, spinup, cache, format); } mfm_harddisk_connector(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); ~mfm_harddisk_connector(); mfm_harddisk_device *get_device(); /* Configuration parameters: encoding = Encoding (see comments in mfm_hd.c) spinupms = Spinup time in milliseconds. Even though this is a property of the physical device, we need a way to configure it per system; some systems expect the hard disk to be turned on before the main system, and expect it to be ready when they try to access it cache = number of cached MFM tracks format = MFMHD_GEN_FORMAT (see formats/mfm_hd.h; currently the only value) */ void configure(mfmhd_enc_t encoding, int spinupms, int cache, mfmhd_format_type format); protected: void device_start() override; void device_config_complete() override; private: mfmhd_enc_t m_encoding; int m_spinupms; int m_cachesize; mfmhd_image_format_t* m_format; }; DECLARE_DEVICE_TYPE(MFM_HD_CONNECTOR, mfm_harddisk_connector) #endif // MAME_DEVICES_IMAGEDEV_MFMHD_H