mirror of
https://github.com/holub/mame
synced 2025-04-22 08:22:15 +03:00
MFM HD work in progress; reading sectors
This commit is contained in:
parent
a444bc8e25
commit
1c92d1000f
@ -74,12 +74,12 @@
|
||||
#define CLK_ADDR 0x0fe0
|
||||
#define RAM_ADDR 0x1000
|
||||
|
||||
#define TRACE_EMU 1
|
||||
#define TRACE_EMU 0
|
||||
#define TRACE_CRU 0
|
||||
#define TRACE_COMP 0
|
||||
#define TRACE_RAM 0
|
||||
#define TRACE_ROM 0
|
||||
#define TRACE_LINES 1
|
||||
#define TRACE_LINES 0
|
||||
#define TRACE_MOTOR 0
|
||||
#define TRACE_DMA 0
|
||||
#define TRACE_INT 0
|
||||
@ -515,12 +515,22 @@ void myarc_hfdc_device::harddisk_index_callback(mfm_harddisk_device *harddisk, i
|
||||
signal_drive_status();
|
||||
}
|
||||
|
||||
/*
|
||||
This is called back from the hard disk when READY becomes asserted.
|
||||
*/
|
||||
void myarc_hfdc_device::harddisk_ready_callback(mfm_harddisk_device *harddisk, int state)
|
||||
{
|
||||
/* if (TRACE_LINES) */ logerror("%s: HD READY = %d\n", tag(), state);
|
||||
set_bits(m_status_latch, HDC_DS_READY, (state==ASSERT_LINE));
|
||||
signal_drive_status();
|
||||
}
|
||||
|
||||
/*
|
||||
This is called back from the hard disk when seek_complete becomes asserted.
|
||||
*/
|
||||
void myarc_hfdc_device::harddisk_skcom_callback(mfm_harddisk_device *harddisk, int state)
|
||||
{
|
||||
/* if (TRACE_LINES) */ if (state==1) logerror("%s: HD seek complete\n", tag());
|
||||
/* if (TRACE_LINES) */ logerror("%s: HD seek complete = %d\n", tag(), state);
|
||||
set_bits(m_status_latch, HDC_DS_SKCOM, (state==ASSERT_LINE));
|
||||
signal_drive_status();
|
||||
}
|
||||
@ -674,6 +684,7 @@ WRITE8_MEMBER( myarc_hfdc_device::auxbus_out )
|
||||
// Dir = 0 -> outward
|
||||
m_current_harddisk->direction_in_w((data & 0x20)? ASSERT_LINE : CLEAR_LINE);
|
||||
m_current_harddisk->step_w((data & 0x10)? ASSERT_LINE : CLEAR_LINE);
|
||||
m_current_harddisk->headsel_w(data & 0x0f);
|
||||
}
|
||||
|
||||
// We are pushing the drive status after OUTPUT2
|
||||
@ -728,6 +739,7 @@ void myarc_hfdc_device::connect_harddisk_unit(int index)
|
||||
if (m_current_harddisk != NULL)
|
||||
{
|
||||
m_current_harddisk->setup_index_pulse_cb(mfm_harddisk_device::index_pulse_cb(FUNC(myarc_hfdc_device::harddisk_index_callback), this));
|
||||
m_current_harddisk->setup_ready_cb(mfm_harddisk_device::ready_cb(FUNC(myarc_hfdc_device::harddisk_ready_callback), this));
|
||||
m_current_harddisk->setup_seek_complete_cb(mfm_harddisk_device::seek_complete_cb(FUNC(myarc_hfdc_device::harddisk_skcom_callback), this));
|
||||
}
|
||||
else
|
||||
@ -1012,9 +1024,9 @@ MACHINE_CONFIG_FRAGMENT( ti99_hfdc )
|
||||
MCFG_FLOPPY_DRIVE_ADD("f4", hfdc_floppies, NULL, myarc_hfdc_device::floppy_formats)
|
||||
|
||||
// NB: Hard disks don't go without image (other than floppy drives)
|
||||
MCFG_MFM_HARDDISK_ADD("h1", hfdc_harddisks, NULL)
|
||||
MCFG_MFM_HARDDISK_ADD("h2", hfdc_harddisks, NULL)
|
||||
MCFG_MFM_HARDDISK_ADD("h3", hfdc_harddisks, NULL)
|
||||
MCFG_MFM_HARDDISK_ADD("h1", hfdc_harddisks, NULL, MFM_BYTE)
|
||||
MCFG_MFM_HARDDISK_ADD("h2", hfdc_harddisks, NULL, MFM_BYTE)
|
||||
MCFG_MFM_HARDDISK_ADD("h3", hfdc_harddisks, NULL, MFM_BYTE)
|
||||
|
||||
MCFG_DEVICE_ADD(CLOCK_TAG, MM58274C, 0)
|
||||
MCFG_MM58274C_MODE24(1) // 24 hour
|
||||
@ -1046,27 +1058,6 @@ ioport_constructor myarc_hfdc_device::device_input_ports() const
|
||||
|
||||
const device_type TI99_HFDC = &device_creator<myarc_hfdc_device>;
|
||||
|
||||
mfm_harddisk_connector::mfm_harddisk_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock):
|
||||
device_t(mconfig, MFM_HD_CONNECTOR, "MFM hard disk connector", tag, owner, clock, "mfm_hd_connector", __FILE__),
|
||||
device_slot_interface(mconfig, *this)
|
||||
{
|
||||
}
|
||||
|
||||
mfm_harddisk_connector::~mfm_harddisk_connector()
|
||||
{
|
||||
}
|
||||
|
||||
mfm_harddisk_device *mfm_harddisk_connector::get_device()
|
||||
{
|
||||
return dynamic_cast<mfm_harddisk_device *>(get_card_device());
|
||||
}
|
||||
|
||||
void mfm_harddisk_connector::device_start()
|
||||
{
|
||||
}
|
||||
|
||||
const device_type MFM_HD_CONNECTOR = &device_creator<mfm_harddisk_connector>;
|
||||
|
||||
// =========================================================================
|
||||
|
||||
/*
|
||||
|
@ -69,6 +69,7 @@ private:
|
||||
// Callbacks for the index hole and seek complete
|
||||
void floppy_index_callback(floppy_image_device *floppy, int state);
|
||||
void harddisk_index_callback(mfm_harddisk_device *harddisk, int state);
|
||||
void harddisk_ready_callback(mfm_harddisk_device *harddisk, int state);
|
||||
void harddisk_skcom_callback(mfm_harddisk_device *harddisk, int state);
|
||||
|
||||
// Operate the floppy motors
|
||||
@ -182,26 +183,6 @@ private:
|
||||
int m_readyflags;
|
||||
};
|
||||
|
||||
/* Connector for a MFM hard disk. See also floppy.c */
|
||||
class mfm_harddisk_connector : public device_t,
|
||||
public device_slot_interface
|
||||
{
|
||||
public:
|
||||
mfm_harddisk_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
~mfm_harddisk_connector();
|
||||
|
||||
mfm_harddisk_device *get_device();
|
||||
|
||||
protected:
|
||||
void device_start();
|
||||
};
|
||||
|
||||
extern const device_type MFM_HD_CONNECTOR;
|
||||
|
||||
#define MCFG_MFM_HARDDISK_ADD(_tag, _slot_intf, _def_slot) \
|
||||
MCFG_DEVICE_ADD(_tag, MFM_HD_CONNECTOR, 0) \
|
||||
MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false);
|
||||
|
||||
// =========================================================================
|
||||
|
||||
/*
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -173,10 +173,16 @@ private:
|
||||
// Timer callback
|
||||
void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
|
||||
|
||||
// Callbacks
|
||||
void ready_callback(int level);
|
||||
void index_callback(int level);
|
||||
void seek_complete_callback(int level);
|
||||
// Handlers for incoming signals
|
||||
void ready_handler();
|
||||
void index_handler();
|
||||
void seek_complete_handler();
|
||||
|
||||
// Wait for this line?
|
||||
bool waiting_for_line(int line, int level);
|
||||
|
||||
// Wait for some other line?
|
||||
bool waiting_for_other_line(int line);
|
||||
|
||||
// Wait for some time to pass or for a line to change level
|
||||
void wait_time(emu_timer *tm, int microsec, int next_substate);
|
||||
@ -212,6 +218,7 @@ private:
|
||||
int byte_counter;
|
||||
bool data_separator_phase;
|
||||
bool last_data_bit;
|
||||
UINT8 clock_reg;
|
||||
UINT8 data_reg;
|
||||
int state;
|
||||
int next_state;
|
||||
@ -241,11 +248,14 @@ private:
|
||||
void rollback();
|
||||
void checkpoint();
|
||||
|
||||
// Found a mark
|
||||
bool found_mark(int state);
|
||||
|
||||
// Delivers the data bits from the given encoding
|
||||
UINT8 get_data_from_encoding(UINT16 raw);
|
||||
|
||||
// ==============================================
|
||||
// PLL functions and interface to floppy
|
||||
// PLL functions and interface to floppy and harddisk
|
||||
// ==============================================
|
||||
|
||||
// Phase-locked loops
|
||||
@ -254,6 +264,9 @@ private:
|
||||
// Clock divider value
|
||||
UINT8 m_clock_divider;
|
||||
|
||||
// MFM HD encoding type
|
||||
mfmhd_enc_t m_hd_encoding;
|
||||
|
||||
// Resets the PLL to the given time
|
||||
void pll_reset(const attotime &when, bool write);
|
||||
|
||||
@ -283,6 +296,9 @@ private:
|
||||
// Skips bytes on the track
|
||||
void skip_on_track(int count, int next_state);
|
||||
|
||||
// Read from the MFM HD
|
||||
bool read_from_mfmhd(const attotime &limit);
|
||||
|
||||
// ==============================================
|
||||
// Command state machine
|
||||
// ==============================================
|
||||
@ -368,6 +384,18 @@ private:
|
||||
// Are we in FM mode?
|
||||
bool fm_mode();
|
||||
|
||||
// Seek completed?
|
||||
bool seek_complete();
|
||||
|
||||
// Are we on track 0?
|
||||
bool on_track00();
|
||||
|
||||
// Are we at the index hole?
|
||||
bool index_hole();
|
||||
|
||||
// Is the attached drive ready?
|
||||
bool drive_ready();
|
||||
|
||||
// Delivers the desired head
|
||||
int desired_head();
|
||||
|
||||
@ -398,12 +426,6 @@ private:
|
||||
// Sector size as read from the track
|
||||
int calc_sector_size();
|
||||
|
||||
// Are we on track 0?
|
||||
bool on_track00();
|
||||
|
||||
// Are we using rapid steps (needed to decide to wait for seek complete)
|
||||
bool rapid_steps();
|
||||
|
||||
// Is the currently selected drive a floppy drive?
|
||||
bool using_floppy();
|
||||
|
||||
|
@ -45,25 +45,36 @@ enum
|
||||
STEP_SETTLE
|
||||
};
|
||||
|
||||
#define TRACKSLOTS 5
|
||||
#define TRACKIMAGE_SIZE 10416 // Provide the buffer for a complete track, including preambles and gaps
|
||||
|
||||
mfm_harddisk_device::mfm_harddisk_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
|
||||
: harddisk_image_device(mconfig, type, name, tag, owner, clock, shortname, source),
|
||||
device_slot_card_interface(mconfig, *this)
|
||||
{
|
||||
}
|
||||
|
||||
mfm_harddisk_device::~mfm_harddisk_device()
|
||||
{
|
||||
}
|
||||
|
||||
void mfm_harddisk_device::device_start()
|
||||
{
|
||||
m_index_timer = timer_alloc(INDEX_TM);
|
||||
m_spinup_timer = timer_alloc(SPINUP_TM);
|
||||
m_seek_timer = timer_alloc(SEEK_TM);
|
||||
m_spinup_time = attotime::from_msec(8000);
|
||||
m_rev_time = attotime::from_hz(60);
|
||||
|
||||
// MFM drives have a revolution rate of 3600 rpm (i.e. 60/sec)
|
||||
m_index_timer->adjust(attotime::from_hz(60), 0, attotime::from_hz(60));
|
||||
|
||||
// Spinup may take up to 24 seconds
|
||||
m_spinup_timer->adjust(attotime::from_msec(8000));
|
||||
m_spinup_timer->adjust(m_spinup_time);
|
||||
|
||||
m_current_cylinder = 10; // for test purpose
|
||||
|
||||
m_cache = global_alloc(mfmhd_trackimage_cache);
|
||||
}
|
||||
|
||||
void mfm_harddisk_device::device_reset()
|
||||
@ -76,16 +87,55 @@ void mfm_harddisk_device::device_reset()
|
||||
m_step_line = CLEAR_LINE;
|
||||
}
|
||||
|
||||
void mfm_harddisk_device::device_stop()
|
||||
{
|
||||
global_free(m_cache);
|
||||
}
|
||||
|
||||
bool mfm_harddisk_device::call_load()
|
||||
{
|
||||
logerror("call_load\n");
|
||||
bool loaded = harddisk_image_device::call_load();
|
||||
if (loaded==IMAGE_INIT_PASS)
|
||||
{
|
||||
m_cache->init(get_chd_file(), tag(), TRACKSLOTS, m_encoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
logerror("Could not load CHD\n");
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
void mfm_harddisk_device::setup_index_pulse_cb(index_pulse_cb cb)
|
||||
{
|
||||
m_index_pulse_cb = cb;
|
||||
}
|
||||
|
||||
void mfm_harddisk_device::setup_ready_cb(ready_cb cb)
|
||||
{
|
||||
m_ready_cb = cb;
|
||||
}
|
||||
|
||||
void mfm_harddisk_device::setup_seek_complete_cb(seek_complete_cb cb)
|
||||
{
|
||||
m_seek_complete_cb = cb;
|
||||
}
|
||||
|
||||
attotime mfm_harddisk_device::track_end_time()
|
||||
{
|
||||
// We back up two microseconds before the track end to avoid the
|
||||
// index pulse to appear earlier (because of rounding effects)
|
||||
attotime nexttime = m_rev_time - attotime::from_nsec(2000);
|
||||
if (!m_ready)
|
||||
{
|
||||
// estimate the next index time; during power-up we assume half the rotational speed
|
||||
// Should never be relevant, though, because READY is false.
|
||||
nexttime = nexttime * 2;
|
||||
}
|
||||
return (m_revolution_start_time.is_never())? attotime::never : (m_revolution_start_time + nexttime);
|
||||
}
|
||||
|
||||
void mfm_harddisk_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
{
|
||||
switch (id)
|
||||
@ -94,6 +144,7 @@ void mfm_harddisk_device::device_timer(emu_timer &timer, device_timer_id id, int
|
||||
/* Simple index hole handling. We assume that there is only a short pulse. */
|
||||
if (!m_index_pulse_cb.isnull())
|
||||
{
|
||||
m_revolution_start_time = machine().time();
|
||||
m_index_pulse_cb(this, ASSERT_LINE);
|
||||
m_index_pulse_cb(this, CLEAR_LINE);
|
||||
}
|
||||
@ -101,6 +152,7 @@ void mfm_harddisk_device::device_timer(emu_timer &timer, device_timer_id id, int
|
||||
case SPINUP_TM:
|
||||
m_ready = true;
|
||||
logerror("%s: Spinup complete, drive is ready\n", tag());
|
||||
if (!m_ready_cb.isnull()) m_ready_cb(this, ASSERT_LINE);
|
||||
break;
|
||||
case SEEK_TM:
|
||||
switch (m_step_phase)
|
||||
@ -128,7 +180,7 @@ void mfm_harddisk_device::device_timer(emu_timer &timer, device_timer_id id, int
|
||||
// Seek completed
|
||||
logerror("%s: Settling done at cylinder %d, seek complete\n", tag(), m_current_cylinder);
|
||||
m_seek_complete = true;
|
||||
m_seek_complete_cb(this, ASSERT_LINE);
|
||||
if (!m_seek_complete_cb.isnull()) m_seek_complete_cb(this, ASSERT_LINE);
|
||||
m_step_phase = STEP_COLLECT;
|
||||
}
|
||||
break;
|
||||
@ -210,7 +262,7 @@ void mfm_harddisk_device::step_w(line_state line)
|
||||
{
|
||||
m_step_phase = STEP_COLLECT;
|
||||
m_seek_complete = false;
|
||||
m_seek_complete_cb(this, CLEAR_LINE);
|
||||
if (!m_seek_complete_cb.isnull()) m_seek_complete_cb(this, CLEAR_LINE);
|
||||
}
|
||||
|
||||
// Counter will be adjusted according to the direction (+-1)
|
||||
@ -226,6 +278,51 @@ void mfm_harddisk_device::step_w(line_state line)
|
||||
m_step_line = line;
|
||||
}
|
||||
|
||||
/*
|
||||
Reading bytes from the hard disk.
|
||||
This is the byte-level access to the hard disk. We deliver the next data byte
|
||||
together with the clock byte.
|
||||
|
||||
Returns true if the time limit will be exceeded before reading the complete byte.
|
||||
Otherwise returns the byte at the given position together with the clock byte.
|
||||
*/
|
||||
bool mfm_harddisk_device::read(attotime &from_when, const attotime &limit, UINT16 &cdata)
|
||||
{
|
||||
UINT16* track = m_cache->get_trackimage(m_current_cylinder, m_current_head);
|
||||
|
||||
if (track==NULL)
|
||||
{
|
||||
// What shall we do in this case?
|
||||
throw emu_fatalerror("Cannot read CHD image");
|
||||
}
|
||||
|
||||
// Calculate the position in the track, given the from_when time and the revolution_start_time.
|
||||
// Each cell takes 100 ns (10 MHz)
|
||||
|
||||
int cell = (from_when - m_revolution_start_time).as_ticks(10000000);
|
||||
|
||||
from_when += attotime::from_nsec(1600);
|
||||
if (from_when > limit) return true;
|
||||
|
||||
int position = cell / 16;
|
||||
|
||||
// Reached the end
|
||||
if (position >= 10416)
|
||||
{
|
||||
m_revolution_start_time += m_rev_time;
|
||||
cell = (from_when - m_revolution_start_time).as_ticks(10000000);
|
||||
position = cell / 16;
|
||||
}
|
||||
|
||||
// TODO: fix the actual issue
|
||||
if (position < 0) position = 0;
|
||||
|
||||
logerror("%s: Reading track %d head %d at position %d\n", tag(), m_current_cylinder, m_current_head, position);
|
||||
cdata = track[position];
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
mfm_hd_generic_device::mfm_hd_generic_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: mfm_harddisk_device(mconfig, MFM_HD_GENERIC, "Generic MFM hard disk (byte level)", tag, owner, clock, "mfm_harddisk", __FILE__)
|
||||
{
|
||||
@ -233,6 +330,402 @@ mfm_hd_generic_device::mfm_hd_generic_device(const machine_config &mconfig, cons
|
||||
|
||||
const device_type MFM_HD_GENERIC = &device_creator<mfm_hd_generic_device>;
|
||||
|
||||
// ===========================================================
|
||||
// Track cache
|
||||
// The cache holds track images to be read by the controller.
|
||||
// This is a write-back LRU cache.
|
||||
// ===========================================================
|
||||
|
||||
mfmhd_trackimage_cache::mfmhd_trackimage_cache():
|
||||
m_tracks(NULL)
|
||||
{
|
||||
m_current_crc = 0;
|
||||
}
|
||||
|
||||
mfmhd_trackimage_cache::~mfmhd_trackimage_cache()
|
||||
{
|
||||
mfmhd_trackimage* current = m_tracks;
|
||||
logerror("%s: MFM HD cache destroy\n", tag());
|
||||
|
||||
// Still dirty?
|
||||
while (current != NULL)
|
||||
{
|
||||
logerror("%s: MFM HD cache: evict line cylinder=%d head=%d\n", tag(), current->cylinder, current->head);
|
||||
if (current->dirty) write_back(current);
|
||||
global_free_array(current->encdata);
|
||||
mfmhd_trackimage* currenttmp = current->next;
|
||||
global_free(current);
|
||||
current = currenttmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Initialize the cache by loading the first <trackslots> tracks.
|
||||
*/
|
||||
void mfmhd_trackimage_cache::init(chd_file* chdfile, const char* dtag, int trackslots, mfmhd_enc_t encoding)
|
||||
{
|
||||
m_encoding = encoding;
|
||||
m_tagdev = dtag;
|
||||
|
||||
logerror("%s: MFM HD cache init; using encoding %d\n", m_tagdev, encoding);
|
||||
chd_error state = CHDERR_NONE;
|
||||
mfmhd_trackimage* previous = NULL;
|
||||
mfmhd_trackimage* current = NULL;
|
||||
std::string metadata;
|
||||
|
||||
m_chd = chdfile;
|
||||
|
||||
if (chdfile==NULL)
|
||||
{
|
||||
logerror("%s: chdfile is null\n", tag());
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the hard disk metadata
|
||||
state = chdfile->read_metadata(HARD_DISK_METADATA_TAG, 0, metadata);
|
||||
if (state != CHDERR_NONE)
|
||||
{
|
||||
throw emu_fatalerror("Failed to read CHD metadata");
|
||||
}
|
||||
|
||||
// Parse the metadata
|
||||
if (sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, &m_cylinders, &m_heads, &m_sectors_per_track, &m_sectorsize) != 4)
|
||||
{
|
||||
throw emu_fatalerror("Invalid metadata");
|
||||
}
|
||||
|
||||
// Load some tracks into the cache
|
||||
for (int i=0; i < trackslots; i++)
|
||||
{
|
||||
logerror("%s: MFM HD allocate cache slot\n", tag());
|
||||
previous = current;
|
||||
current = global_alloc(mfmhd_trackimage);
|
||||
current->encdata = global_alloc_array(UINT16, TRACKIMAGE_SIZE);
|
||||
|
||||
// Load the first tracks into the slots
|
||||
state = load_track(current, i, 0, 32, 256, 4);
|
||||
current->next = NULL;
|
||||
|
||||
if (state != CHDERR_NONE) throw emu_fatalerror("Cannot load cylinder %d head %d from hard disk", i, 0);
|
||||
|
||||
if (previous != NULL)
|
||||
previous->next = current;
|
||||
else
|
||||
// Head
|
||||
m_tracks = current;
|
||||
}
|
||||
|
||||
current = m_tracks;
|
||||
|
||||
while (current != NULL)
|
||||
{
|
||||
logerror("%s: MFM HD cache: containing line cylinder=%d head=%d\n", tag(), current->cylinder, current->head);
|
||||
mfmhd_trackimage* currenttmp = current->next;
|
||||
current = currenttmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the linear sector number, given the CHS data.
|
||||
|
||||
C,H,S
|
||||
| 0,0,0 | 0,0,1 | 0,0,2 | ...
|
||||
| 0,1,0 | 0,1,1 | 0,1,2 | ...
|
||||
...
|
||||
| 1,0,0 | ...
|
||||
...
|
||||
*/
|
||||
int mfmhd_trackimage_cache::chs_to_lba(int cylinder, int head, int sector)
|
||||
{
|
||||
if ((cylinder < m_cylinders) && (head < m_heads) && (sector < m_sectors_per_track))
|
||||
{
|
||||
return (cylinder * m_heads + head) * m_sectors_per_track + sector;
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
Delivers the track image.
|
||||
First look up the track image in the cache. If not present, load it from
|
||||
the CHD, convert it, and evict the least recently used line.
|
||||
The searched track will be the first in m_tracks.
|
||||
*/
|
||||
UINT16* mfmhd_trackimage_cache::get_trackimage(int cylinder, int head)
|
||||
{
|
||||
// Search the cached track images
|
||||
mfmhd_trackimage* current = m_tracks;
|
||||
mfmhd_trackimage* previous = NULL;
|
||||
|
||||
chd_error state = CHDERR_NONE;
|
||||
|
||||
// Repeat the search. This loop should run at most twice; once for a direct hit,
|
||||
// and twice on miss, then the second iteration will be a hit.
|
||||
while (state == CHDERR_NONE)
|
||||
{
|
||||
// A simple linear search
|
||||
while (current != NULL)
|
||||
{
|
||||
if (current->cylinder == cylinder && current->head == head)
|
||||
{
|
||||
// found it
|
||||
// move it to the front of the chain for LRU
|
||||
if (previous != NULL)
|
||||
{
|
||||
previous->next = current->next; // pull out here
|
||||
current->next = m_tracks; // put the previous head into the next field
|
||||
m_tracks = current; // set this line as new head
|
||||
}
|
||||
return current->encdata;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't lose the pointer to the next tail
|
||||
if (current->next != NULL) previous = current;
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
// If we are here, we have a cache miss.
|
||||
// Evict the last line
|
||||
// Load the new line into that line
|
||||
// Then look it up again, which will move it to the front
|
||||
|
||||
// previous points to the second to last element
|
||||
current = previous->next;
|
||||
logerror("%s: MFM HD cache: evict line cylinder=%d head=%d\n", tag(), current->cylinder, current->head);
|
||||
|
||||
if (current->dirty) write_back(current);
|
||||
state = load_track(current, cylinder, head, 32, 256, 4);
|
||||
}
|
||||
// If we are here we have a CHD error.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
Create MFM encoding.
|
||||
*/
|
||||
void mfmhd_trackimage_cache::mfm_encode(mfmhd_trackimage* slot, int& position, UINT8 byte, int count)
|
||||
{
|
||||
mfm_encode_mask(slot, position, byte, count, 0x00);
|
||||
}
|
||||
|
||||
void mfmhd_trackimage_cache::mfm_encode_a1(mfmhd_trackimage* slot, int& position)
|
||||
{
|
||||
m_current_crc = 0xffff;
|
||||
mfm_encode_mask(slot, position, 0xa1, 1, 0x04);
|
||||
// 0x443b; CRC for A1
|
||||
}
|
||||
|
||||
void mfmhd_trackimage_cache::mfm_encode_mask(mfmhd_trackimage* slot, int& position, UINT8 byte, int count, int mask)
|
||||
{
|
||||
UINT16 encclock = 0;
|
||||
UINT16 encdata = 0;
|
||||
UINT8 thisbyte = byte;
|
||||
bool mark = (mask != 0x00);
|
||||
|
||||
m_current_crc = ccitt_crc16_one(m_current_crc, byte);
|
||||
|
||||
for (int i=0; i < 8; i++)
|
||||
{
|
||||
encdata <<= 1;
|
||||
encclock <<= 1;
|
||||
|
||||
if (m_encoding == MFM_BITS || m_encoding == MFM_BYTE)
|
||||
{
|
||||
// skip one position for later interleaving
|
||||
encdata <<= 1;
|
||||
encclock <<= 1;
|
||||
}
|
||||
|
||||
if (thisbyte & 0x80)
|
||||
{
|
||||
// Encoding 1 => 01
|
||||
encdata |= 1;
|
||||
m_lastbit = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Encoding 0 => x0
|
||||
// If the bit in the mask is set, suppress the clock bit
|
||||
// Also, if we use the simplified encoding, don't set the clock bits
|
||||
if (m_lastbit == false && m_encoding != SEPARATED_SIMPLE && (mask & 0x80) == 0) encclock |= 1;
|
||||
m_lastbit = false;
|
||||
}
|
||||
mask <<= 1;
|
||||
// For simplified encoding, set all clock bits to indicate a mark
|
||||
if (m_encoding == SEPARATED_SIMPLE && mark) encclock |= 1;
|
||||
thisbyte <<= 1;
|
||||
}
|
||||
|
||||
if (m_encoding == MFM_BITS || m_encoding == MFM_BYTE)
|
||||
encclock <<= 1;
|
||||
else
|
||||
encclock <<= 8;
|
||||
|
||||
slot->encdata[position++] = (encclock | encdata);
|
||||
|
||||
// When we write the byte multiple times, check whether the next encoding
|
||||
// differs from the previous because of the last bit
|
||||
|
||||
if (m_encoding == MFM_BITS || m_encoding == MFM_BYTE)
|
||||
{
|
||||
encclock &= 0x7fff;
|
||||
if ((byte & 0x80)==0 && m_lastbit==false) encclock |= 0x8000;
|
||||
}
|
||||
|
||||
for (int j=1; j < count; j++)
|
||||
{
|
||||
slot->encdata[position++] = (encclock | encdata);
|
||||
m_current_crc = ccitt_crc16_one(m_current_crc, byte);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Load a track from the CHD.
|
||||
*/
|
||||
chd_error mfmhd_trackimage_cache::load_track(mfmhd_trackimage* slot, int cylinder, int head, int sectorcount, int size, int interleave)
|
||||
{
|
||||
chd_error state = CHDERR_NONE;
|
||||
|
||||
UINT8 sector_content[1024];
|
||||
|
||||
logerror("%s: MFM HD cache: load cylinder=%d head=%d from CHD\n", tag(), cylinder, head);
|
||||
|
||||
m_lastbit = false;
|
||||
int position = 0; // will be incremented by each encode call
|
||||
|
||||
// Gap 1
|
||||
mfm_encode(slot, position, 0x4e, 16);
|
||||
|
||||
int sec_il_start = 0;
|
||||
int sec_number = 0;
|
||||
|
||||
// Round up
|
||||
int delta = (sectorcount + interleave-1) / interleave;
|
||||
|
||||
logerror("%02x %02x: ", cylinder&0xff, head&0xff);
|
||||
for (int sector = 0; sector < sectorcount; sector++)
|
||||
{
|
||||
logerror("%02d ", sec_number);
|
||||
// Sync gap
|
||||
mfm_encode(slot, position, 0x00, 13);
|
||||
|
||||
// Write IDAM
|
||||
mfm_encode_a1(slot, position);
|
||||
mfm_encode(slot, position, 0xfe); // ID field?
|
||||
|
||||
// Write header
|
||||
mfm_encode(slot, position, cylinder & 0xff);
|
||||
mfm_encode(slot, position, head & 0xff);
|
||||
mfm_encode(slot, position, sec_number);
|
||||
mfm_encode(slot, position, (size >> 7)-1);
|
||||
|
||||
// Write CRC for header.
|
||||
int crc = m_current_crc;
|
||||
mfm_encode(slot, position, (crc >> 8) & 0xff);
|
||||
mfm_encode(slot, position, crc & 0xff);
|
||||
|
||||
// Gap 2
|
||||
mfm_encode(slot, position, 0x4e, 3);
|
||||
|
||||
// Sync
|
||||
mfm_encode(slot, position, 0x00, 13);
|
||||
|
||||
// Write DAM
|
||||
mfm_encode_a1(slot, position);
|
||||
mfm_encode(slot, position, 0xfb);
|
||||
|
||||
// Get sector content from CHD
|
||||
chd_error state = m_chd->read_units(chs_to_lba(cylinder, head, sec_number), sector_content);
|
||||
if (state != CHDERR_NONE)
|
||||
break;
|
||||
|
||||
for (int i=0; i < size; i++)
|
||||
mfm_encode(slot, position, sector_content[i]);
|
||||
|
||||
// Write CRC for content.
|
||||
crc = m_current_crc;
|
||||
mfm_encode(slot, position, (crc >> 8) & 0xff);
|
||||
mfm_encode(slot, position, crc & 0xff);
|
||||
|
||||
// Gap 3
|
||||
mfm_encode(slot, position, 0x00, 3);
|
||||
mfm_encode(slot, position, 0x4e, 19);
|
||||
|
||||
// Calculate next sector number
|
||||
sec_number += delta;
|
||||
if (sec_number >= sectorcount) sec_number = ++sec_il_start;
|
||||
}
|
||||
|
||||
// Gap 4
|
||||
if (state == CHDERR_NONE)
|
||||
{
|
||||
// Fill the rest with 0x4e
|
||||
mfm_encode(slot, position, 0x4e, TRACKIMAGE_SIZE-position);
|
||||
logerror("\n");
|
||||
showtrack(slot->encdata, TRACKIMAGE_SIZE);
|
||||
}
|
||||
|
||||
slot->dirty = false;
|
||||
slot->cylinder = cylinder;
|
||||
slot->head = head;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Maybe use a scheduled write-back in addition to the eviction.
|
||||
*/
|
||||
void mfmhd_trackimage_cache::write_back(mfmhd_trackimage* slot)
|
||||
{
|
||||
logerror("%s: MFM HD cache: write back cylinder=%d head=%d to CHD\n", tag(), slot->cylinder, slot->head);
|
||||
slot->dirty = false;
|
||||
}
|
||||
|
||||
/*
|
||||
For debugging. Outputs the byte array in a xxd-like way.
|
||||
*/
|
||||
void mfmhd_trackimage_cache::showtrack(UINT16* enctrack, int length)
|
||||
{
|
||||
for (int i=0; i < length; i+=16)
|
||||
{
|
||||
logerror("%07x: ", i);
|
||||
for (int j=0; j < 16; j++)
|
||||
{
|
||||
logerror("%04x ", enctrack[i+j]);
|
||||
}
|
||||
logerror(" ");
|
||||
logerror("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
|
||||
mfm_harddisk_connector::mfm_harddisk_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock):
|
||||
device_t(mconfig, MFM_HD_CONNECTOR, "MFM hard disk connector", tag, owner, clock, "mfm_hd_connector", __FILE__),
|
||||
device_slot_interface(mconfig, *this)
|
||||
{
|
||||
}
|
||||
|
||||
mfm_harddisk_connector::~mfm_harddisk_connector()
|
||||
{
|
||||
}
|
||||
|
||||
mfm_harddisk_device* mfm_harddisk_connector::get_device()
|
||||
{
|
||||
return dynamic_cast<mfm_harddisk_device *>(get_card_device());
|
||||
}
|
||||
|
||||
void mfm_harddisk_connector::device_config_complete()
|
||||
{
|
||||
mfm_harddisk_device *dev = get_device();
|
||||
if (dev != NULL)
|
||||
dev->set_encoding(m_encoding);
|
||||
}
|
||||
|
||||
const device_type MFM_HD_CONNECTOR = &device_creator<mfm_harddisk_connector>;
|
||||
|
||||
// ================================================================
|
||||
|
||||
// ===========================================================================
|
||||
// Legacy implementation
|
||||
// ===========================================================================
|
||||
|
@ -17,38 +17,112 @@
|
||||
#include "emu.h"
|
||||
#include "imagedev/harddriv.h"
|
||||
|
||||
/*
|
||||
Determine how data are passed from the hard disk to the controller. We
|
||||
allow for different degrees of hardware emulation.
|
||||
*/
|
||||
enum mfmhd_enc_t
|
||||
{
|
||||
MFM_BITS, // One bit at a time
|
||||
MFM_BYTE, // One data byte with interleaved clock bits
|
||||
SEPARATED, // 8 clock bits (most sig byte), one data byte (least sig byte)
|
||||
SEPARATED_SIMPLE // MSB: 00/FF (standard / mark) clock, LSB: one data byte
|
||||
};
|
||||
|
||||
class mfmhd_trackimage
|
||||
{
|
||||
public:
|
||||
bool dirty;
|
||||
int cylinder;
|
||||
int head;
|
||||
UINT16* encdata; // MFM encoding per byte
|
||||
mfmhd_trackimage* next;
|
||||
};
|
||||
|
||||
class mfmhd_trackimage_cache
|
||||
{
|
||||
public:
|
||||
mfmhd_trackimage_cache();
|
||||
~mfmhd_trackimage_cache();
|
||||
void init(chd_file* chdfile, const char* tag, int trackslots, mfmhd_enc_t encoding);
|
||||
UINT16* get_trackimage(int cylinder, int head);
|
||||
|
||||
private:
|
||||
void mfm_encode(mfmhd_trackimage* slot, int& position, UINT8 byte, int count=1);
|
||||
void mfm_encode_a1(mfmhd_trackimage* slot, int& position);
|
||||
void mfm_encode_mask(mfmhd_trackimage* slot, int& position, UINT8 byte, int count, int mask);
|
||||
chd_error load_track(mfmhd_trackimage* slot, int cylinder, int head, int sectorcount, int size, int interleave);
|
||||
void write_back(mfmhd_trackimage* timg);
|
||||
int chs_to_lba(int cylinder, int head, int sector);
|
||||
chd_file* m_chd;
|
||||
|
||||
const char* m_tagdev;
|
||||
mfmhd_trackimage* m_tracks;
|
||||
mfmhd_enc_t m_encoding;
|
||||
bool m_lastbit;
|
||||
int m_current_crc;
|
||||
int m_cylinders;
|
||||
int m_heads;
|
||||
int m_sectors_per_track;
|
||||
int m_sectorsize;
|
||||
void showtrack(UINT16* enctrack, int length);
|
||||
const char* tag() { return m_tagdev; }
|
||||
};
|
||||
|
||||
class mfm_harddisk_device : public harddisk_image_device,
|
||||
public device_slot_card_interface
|
||||
{
|
||||
public:
|
||||
mfm_harddisk_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
|
||||
~mfm_harddisk_device();
|
||||
|
||||
typedef delegate<void (mfm_harddisk_device*, int)> index_pulse_cb;
|
||||
typedef delegate<void (mfm_harddisk_device*, int)> ready_cb;
|
||||
typedef delegate<void (mfm_harddisk_device*, int)> 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);
|
||||
|
||||
void set_encoding(mfmhd_enc_t encoding) { m_encoding = encoding; }
|
||||
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 &data);
|
||||
|
||||
// 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; }
|
||||
|
||||
bool call_load();
|
||||
|
||||
// Tells us the time when the track ends (next index pulse)
|
||||
attotime track_end_time();
|
||||
|
||||
protected:
|
||||
void device_start();
|
||||
void device_stop();
|
||||
void device_reset();
|
||||
emu_timer *m_index_timer, *m_spinup_timer, *m_seek_timer;
|
||||
index_pulse_cb m_index_pulse_cb;
|
||||
seek_complete_cb m_seek_complete_cb;
|
||||
void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
|
||||
|
||||
emu_timer *m_index_timer, *m_spinup_timer, *m_seek_timer;
|
||||
index_pulse_cb m_index_pulse_cb;
|
||||
ready_cb m_ready_cb;
|
||||
seek_complete_cb m_seek_complete_cb;
|
||||
|
||||
private:
|
||||
mfmhd_enc_t m_encoding;
|
||||
bool m_ready;
|
||||
int m_current_cylinder;
|
||||
int m_current_head;
|
||||
int m_track_delta;
|
||||
int m_step_phase;
|
||||
bool m_seek_complete;
|
||||
@ -57,6 +131,13 @@ private:
|
||||
bool m_autotruncation;
|
||||
line_state m_step_line; // keep the last state
|
||||
|
||||
attotime m_spinup_time;
|
||||
attotime m_revolution_start_time;
|
||||
attotime m_rev_time;
|
||||
|
||||
mfmhd_trackimage_cache* m_cache;
|
||||
|
||||
void prepare_track(int cylinder, int head);
|
||||
void head_move();
|
||||
};
|
||||
|
||||
@ -68,6 +149,33 @@ public:
|
||||
|
||||
extern const device_type MFM_HD_GENERIC;
|
||||
|
||||
/* Connector for a MFM hard disk. See also floppy.c */
|
||||
class mfm_harddisk_connector : public device_t,
|
||||
public device_slot_interface
|
||||
{
|
||||
public:
|
||||
mfm_harddisk_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
~mfm_harddisk_connector();
|
||||
|
||||
mfm_harddisk_device *get_device();
|
||||
|
||||
void set_encoding(mfmhd_enc_t encoding) { m_encoding = encoding; }
|
||||
|
||||
protected:
|
||||
void device_start() { };
|
||||
void device_config_complete();
|
||||
|
||||
private:
|
||||
mfmhd_enc_t m_encoding;
|
||||
};
|
||||
|
||||
extern const device_type MFM_HD_CONNECTOR;
|
||||
|
||||
#define MCFG_MFM_HARDDISK_ADD(_tag, _slot_intf, _def_slot, _enc) \
|
||||
MCFG_DEVICE_ADD(_tag, MFM_HD_CONNECTOR, 0) \
|
||||
MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) \
|
||||
static_cast<mfm_harddisk_connector *>(device)->set_encoding(_enc);
|
||||
|
||||
// ===========================================================================
|
||||
// Legacy implementation
|
||||
// ===========================================================================
|
||||
|
Loading…
Reference in New Issue
Block a user