MFM HD work in progress; reading sectors

This commit is contained in:
Michael Zapf 2015-06-04 19:50:31 +02:00
parent a444bc8e25
commit 1c92d1000f
6 changed files with 1172 additions and 168 deletions

View File

@ -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>;
// =========================================================================
/*

View File

@ -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

View File

@ -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();

View File

@ -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
// ===========================================================================

View File

@ -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
// ===========================================================================