mirror of
https://github.com/holub/mame
synced 2025-04-19 15:11:37 +03:00
upd7261: new device
This commit is contained in:
parent
37c9b6ec80
commit
d939557e4c
@ -5365,3 +5365,14 @@ if (MACHINES["TC9223"]~=null) then
|
||||
MAME_DIR .. "src/devices/machine/tc9223.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--@src/devices/machine/upd7261.h,MACHINES["UPD7261"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (MACHINES["UPD7261"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/machine/upd7261.cpp",
|
||||
MAME_DIR .. "src/devices/machine/upd7261.h",
|
||||
}
|
||||
end
|
||||
|
733
src/devices/machine/upd7261.cpp
Normal file
733
src/devices/machine/upd7261.cpp
Normal file
@ -0,0 +1,733 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Patrick Mackinlay
|
||||
|
||||
/*
|
||||
* NEC μPD7261A/μPD7261B Hard-Disk Controller
|
||||
*
|
||||
* Sources:
|
||||
* - μPD7261A/μPD7261B Hard-Disk Controllers, © NEC Electronics Inc.
|
||||
*
|
||||
* TODO:
|
||||
* - unemulated commands
|
||||
* - hard sectored drives
|
||||
* - concurrent recalibrate/seek commands
|
||||
* - terminal count
|
||||
* - drive/head mapping for 3b2
|
||||
*/
|
||||
|
||||
#include "emu.h"
|
||||
#include "upd7261.h"
|
||||
|
||||
#define LOG_GENERAL (1U << 0)
|
||||
#define LOG_REGR (1U << 1)
|
||||
#define LOG_REGW (1U << 2)
|
||||
#define LOG_COMMAND (1U << 3)
|
||||
#define LOG_STATE (1U << 4)
|
||||
#define LOG_DMA (1U << 5)
|
||||
|
||||
//#define VERBOSE (LOG_GENERAL|LOG_COMMAND)
|
||||
#include "logmacro.h"
|
||||
|
||||
unsigned constexpr BUF_SIZE = 4096;
|
||||
|
||||
enum state : u32
|
||||
{
|
||||
IDLE = 0,
|
||||
|
||||
EXECUTE_READ,
|
||||
EXECUTE_WRITE,
|
||||
|
||||
SEEK_POLLED0, // recalibrate/seek with polling
|
||||
SEEK_POLLED1, // recalibrate/seek with polling
|
||||
|
||||
RESULTS_4, // detect error
|
||||
RESULTS_56, // results: recalibrate, seek
|
||||
RESULTS_bcdef, // results: read data, check, scan, verify data, write data
|
||||
RESULTS_789, // results: format, verify id, read id
|
||||
|
||||
COMPLETE,
|
||||
ERROR,
|
||||
};
|
||||
|
||||
enum status_mask : u8
|
||||
{
|
||||
S_DRQ = 0x01, // data request
|
||||
S_NCI = 0x02, // not coincident
|
||||
S_IER = 0x04, // ID error
|
||||
S_RRQ = 0x08, // reset request
|
||||
S_SRQ = 0x10, // sense interrupt status request
|
||||
S_CEL = 0x20, // command end lo
|
||||
S_CEH = 0x40, // command end hi
|
||||
S_CB = 0x80, // controller busy
|
||||
};
|
||||
|
||||
enum ist_mask : u8
|
||||
{
|
||||
IST_UA = 0x07, // unit address
|
||||
IST_NR = 0x08, // not ready
|
||||
IST_EQC = 0x10, // equipment check
|
||||
IST_SER = 0x20, // seek error
|
||||
IST_RC = 0x40, // ready change
|
||||
IST_SEN = 0x80, // seek end
|
||||
};
|
||||
|
||||
enum ust_mask : u8
|
||||
{
|
||||
UST_D0 = 0x01, // fault/write fault
|
||||
UST_D1 = 0x02, // seek error/ready
|
||||
UST_D2 = 0x04, // on cylinder/track 000
|
||||
UST_D3 = 0x08, // unit ready/seek complete
|
||||
UST_D4 = 0x10, // -/drive selected
|
||||
UST_D5 = 0x20, // write protected/-
|
||||
UST_D6 = 0x40, // seek end/-
|
||||
UST_D7 = 0x80, // unit selected/-
|
||||
};
|
||||
|
||||
enum est_mask : u8
|
||||
{
|
||||
EST_MAM = 0x01, // missing address mark
|
||||
EST_NWR = 0x02, // not writable
|
||||
EST_ND = 0x04, // no data
|
||||
EST_NR = 0x08, // not ready
|
||||
EST_EQC = 0x10, // equipment check
|
||||
EST_DER = 0x20, // data error
|
||||
EST_OVR = 0x40, // overrun
|
||||
EST_ENC = 0x80, // end of cylinder
|
||||
};
|
||||
|
||||
enum specify_mode_mask : u8
|
||||
{
|
||||
SM_STP = 0x0f, // stepping rate
|
||||
SM_SSEC = 0x10, // soft-sector disk
|
||||
SM_CRCS = 0x20, // generator polynomial
|
||||
SM_ECC = 0x40, // ecc is appended in data field
|
||||
};
|
||||
|
||||
enum specify_dtlh_mask : u8
|
||||
{
|
||||
DTLH_DTLH = 0x0f, // data length high bits
|
||||
DTLH_NPOL = 0x10, // non-polling mode
|
||||
DTLH_PAD = 0x20, // ID/data pad (0=0x00, 1=0x4e)
|
||||
DTLH_CRC = 0x40, // initial polynomial counter
|
||||
};
|
||||
|
||||
DEFINE_DEVICE_TYPE(UPD7261, upd7261_device, "upd7261", "NEC uPD7261 Hard-Disk Controller")
|
||||
|
||||
upd7261_device::upd7261_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
|
||||
: device_t(mconfig, UPD7261, tag, owner, clock)
|
||||
, m_drive(*this, "%u", 0)
|
||||
, m_dreq(*this)
|
||||
, m_int(*this)
|
||||
, m_head(0)
|
||||
, m_specify{}
|
||||
, m_transfer{}
|
||||
, m_dreq_state(false)
|
||||
, m_int_state(false)
|
||||
, m_tc_state(false)
|
||||
{
|
||||
}
|
||||
|
||||
void upd7261_device::device_start()
|
||||
{
|
||||
save_item(NAME(m_state));
|
||||
save_item(NAME(m_head));
|
||||
|
||||
save_item(NAME(m_status));
|
||||
save_item(NAME(m_est));
|
||||
save_item(NAME(m_ist));
|
||||
save_item(NAME(m_ua));
|
||||
save_item(NAME(m_pcn));
|
||||
|
||||
save_item(STRUCT_MEMBER(m_specify, mode));
|
||||
save_item(STRUCT_MEMBER(m_specify, dtlh));
|
||||
save_item(STRUCT_MEMBER(m_specify, dtll));
|
||||
save_item(STRUCT_MEMBER(m_specify, etn));
|
||||
save_item(STRUCT_MEMBER(m_specify, esn));
|
||||
save_item(STRUCT_MEMBER(m_specify, gpl2));
|
||||
save_item(STRUCT_MEMBER(m_specify, rwch));
|
||||
save_item(STRUCT_MEMBER(m_specify, rwcl));
|
||||
|
||||
save_item(STRUCT_MEMBER(m_transfer, phn));
|
||||
save_item(STRUCT_MEMBER(m_transfer, lcnh));
|
||||
save_item(STRUCT_MEMBER(m_transfer, lcnl));
|
||||
save_item(STRUCT_MEMBER(m_transfer, lhn));
|
||||
save_item(STRUCT_MEMBER(m_transfer, lsn));
|
||||
save_item(STRUCT_MEMBER(m_transfer, scnt));
|
||||
|
||||
save_item(NAME(m_buf_index));
|
||||
save_item(NAME(m_buf_count));
|
||||
save_pointer(NAME(m_buf), BUF_SIZE);
|
||||
|
||||
save_item(NAME(m_dreq_state));
|
||||
save_item(NAME(m_int_state));
|
||||
save_item(NAME(m_tc_state));
|
||||
|
||||
m_state_timer = timer_alloc(timer_expired_delegate(FUNC(upd7261_device::state_timer), this));
|
||||
|
||||
m_buf = std::make_unique<u8[]>(BUF_SIZE);
|
||||
}
|
||||
|
||||
void upd7261_device::device_reset()
|
||||
{
|
||||
m_state = IDLE;
|
||||
|
||||
m_status = 0;
|
||||
m_est = 0;
|
||||
m_ist = 0;
|
||||
m_ua = 0;
|
||||
|
||||
for (u16 &pcn : m_pcn)
|
||||
pcn = 0;
|
||||
|
||||
m_buf_index = 0;
|
||||
m_buf_count = 0;
|
||||
|
||||
set_dreq(false);
|
||||
set_int(false);
|
||||
}
|
||||
|
||||
void upd7261_device::map(address_map &map)
|
||||
{
|
||||
map(0x0, 0x0).rw(FUNC(upd7261_device::data_r), FUNC(upd7261_device::data_w));
|
||||
map(0x1, 0x1).rw(FUNC(upd7261_device::status_r), FUNC(upd7261_device::command_w));
|
||||
}
|
||||
|
||||
void upd7261_device::set_dreq(int state)
|
||||
{
|
||||
if (state != m_dreq_state)
|
||||
{
|
||||
LOGMASKED(LOG_STATE, "dreq %d\n", state);
|
||||
|
||||
if (state)
|
||||
{
|
||||
m_status |= S_DRQ;
|
||||
m_buf_count = m_specify.dtl();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status &= ~S_DRQ;
|
||||
m_buf_count = 0;
|
||||
}
|
||||
|
||||
m_dreq_state = state;
|
||||
m_dreq(m_dreq_state);
|
||||
}
|
||||
}
|
||||
|
||||
void upd7261_device::set_int(bool state)
|
||||
{
|
||||
if (state != m_int_state)
|
||||
{
|
||||
LOGMASKED(LOG_STATE, "int %d\n", state);
|
||||
m_int_state = state;
|
||||
m_int(m_int_state);
|
||||
}
|
||||
}
|
||||
|
||||
void upd7261_device::tc_w(int state)
|
||||
{
|
||||
LOGMASKED(LOG_STATE, "tc %d\n", state);
|
||||
|
||||
m_tc_state = !state;
|
||||
}
|
||||
|
||||
u8 upd7261_device::data_r()
|
||||
{
|
||||
if (m_buf_index == m_buf_count)
|
||||
fatalerror("%s: buffer underrun\n", tag());
|
||||
|
||||
u8 const data = m_buf[m_buf_index++];
|
||||
|
||||
if (m_status & S_DRQ)
|
||||
LOGMASKED(LOG_DMA, "data_r 0x%02x (dma)\n", data);
|
||||
else
|
||||
LOGMASKED(LOG_REGR, "data_r 0x%02x (%s)\n", data, machine().describe_context());
|
||||
|
||||
if ((m_status & S_DRQ) && (m_buf_index == m_buf_count))
|
||||
m_state_timer->adjust(attotime::zero);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void upd7261_device::data_w(u8 data)
|
||||
{
|
||||
if (m_status & S_DRQ)
|
||||
LOGMASKED(LOG_DMA, "data_w 0x%02x (dma)\n", data);
|
||||
else
|
||||
LOGMASKED(LOG_REGW, "data_w 0x%02x (%s)\n", data, machine().describe_context());
|
||||
|
||||
if (m_buf_index == BUF_SIZE)
|
||||
fatalerror("%s: buffer overrun\n", tag());
|
||||
|
||||
m_buf[m_buf_index++] = data;
|
||||
|
||||
if ((m_status & S_DRQ) && (m_buf_index == m_buf_count))
|
||||
m_state_timer->adjust(attotime::zero);
|
||||
}
|
||||
|
||||
u8 upd7261_device::status_r()
|
||||
{
|
||||
u8 data = m_status;
|
||||
|
||||
LOGMASKED(LOG_REGR, "status_r 0x%02x (%s)\n", data, machine().describe_context());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void upd7261_device::command_w(u8 data)
|
||||
{
|
||||
LOGMASKED(LOG_REGW, "command_w 0x%02x (%s)\n", data, machine().describe_context());
|
||||
static constexpr attotime execute = attotime::from_nsec(400);
|
||||
|
||||
if (BIT(data, 4, 4))
|
||||
{
|
||||
m_status &= ~(S_CEH | S_CEL);
|
||||
m_status |= S_CB;
|
||||
|
||||
m_ua = BIT(data, 0, (m_specify.mode & SM_SSEC) ? 2 : 3);
|
||||
|
||||
m_est = 0;
|
||||
|
||||
switch (BIT(data, 4, 4))
|
||||
{
|
||||
case 0x1: // sense interrupt status
|
||||
LOGMASKED(LOG_COMMAND, "sense interrupt status 0x%02x\n", m_ist);
|
||||
m_buf_index = 0;
|
||||
m_buf_count = 0;
|
||||
|
||||
if (m_status & S_SRQ)
|
||||
{
|
||||
m_buf[m_buf_count++] = m_ist;
|
||||
m_status &= ~S_SRQ;
|
||||
m_state = COMPLETE;
|
||||
}
|
||||
else
|
||||
m_state = ERROR;
|
||||
|
||||
m_ist = 0;
|
||||
m_state_timer->adjust(execute);
|
||||
break;
|
||||
case 0x2: // specify
|
||||
m_specify.mode = m_buf[0];
|
||||
m_specify.dtlh = m_buf[1];
|
||||
m_specify.dtll = m_buf[2];
|
||||
m_specify.etn = m_buf[3];
|
||||
m_specify.esn = m_buf[4];
|
||||
m_specify.gpl2 = m_buf[5];
|
||||
m_specify.rwch = m_buf[6];
|
||||
m_specify.rwcl = m_buf[7];
|
||||
|
||||
m_buf_index = 0;
|
||||
m_buf_count = 0;
|
||||
|
||||
LOGMASKED(LOG_COMMAND, "specify mode 0x%02x dtl 0x%04x etn 0x%02x esn 0x%02x gpl2 0x%02x rwch 0x%02x rwcl 0x%02x\n",
|
||||
m_specify.mode, m_specify.dtl(), m_specify.etn, m_specify.esn, m_specify.gpl2, m_specify.rwch, m_specify.rwcl);
|
||||
|
||||
if (!(m_specify.mode & SM_SSEC))
|
||||
fatalerror("%s: hard sectored mode is not emulated\n", tag());
|
||||
|
||||
m_state = COMPLETE;
|
||||
m_state_timer->adjust(execute);
|
||||
break;
|
||||
case 0x3: // sense unit status
|
||||
LOGMASKED(LOG_COMMAND, "sense unit status\n");
|
||||
m_buf_index = 0;
|
||||
m_buf_count = 0;
|
||||
|
||||
if (m_drive[m_ua] && m_drive[m_ua]->exists())
|
||||
m_buf[m_buf_count++] = UST_D4 | UST_D1;
|
||||
else
|
||||
m_buf[m_buf_count++] = 0;
|
||||
|
||||
m_state = COMPLETE;
|
||||
m_state_timer->adjust(execute);
|
||||
break;
|
||||
case 0x4: // detect error
|
||||
LOGMASKED(LOG_COMMAND, "detect error (not emulated)\n");
|
||||
m_buf_index = 0;
|
||||
m_buf_count = 0;
|
||||
|
||||
m_state = RESULTS_4;
|
||||
m_state_timer->adjust(execute);
|
||||
break;
|
||||
case 0x5: // recalibrate
|
||||
LOGMASKED(LOG_COMMAND, "recalibrate (%s mode with polling %s)\n",
|
||||
BIT(data, 3) ? "buffered" : "normal", (m_specify.dtlh & DTLH_NPOL) ? "disabled" : "enabled");
|
||||
m_buf_index = 0;
|
||||
m_buf_count = 0;
|
||||
|
||||
if (m_specify.mode & SM_SSEC)
|
||||
{
|
||||
if (m_specify.dtlh & DTLH_NPOL)
|
||||
{
|
||||
// non-polled mode
|
||||
m_buf[m_buf_count++] = IST_SEN | m_ua;
|
||||
m_state = COMPLETE;
|
||||
|
||||
m_state_timer->adjust(attotime::from_ticks(m_specify.stp(m_pcn[m_ua]), clock()));
|
||||
m_pcn[m_ua] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// polled mode
|
||||
m_state = SEEK_POLLED0;
|
||||
m_state_timer->adjust(execute, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x6: // seek
|
||||
LOGMASKED(LOG_COMMAND, "seek pcn 0x%04x (%s mode with polling %s)\n",
|
||||
u16(m_buf[0]) << 8 | m_buf[1], BIT(data, 3) ? "buffered" : "normal",
|
||||
(m_specify.dtlh & DTLH_NPOL) ? "disabled" : "enabled");
|
||||
|
||||
m_buf_index = 0;
|
||||
m_buf_count = 0;
|
||||
if (m_specify.mode & SM_SSEC)
|
||||
{
|
||||
u16 const pcn = u16(m_buf[0]) << 8 | m_buf[1];
|
||||
|
||||
if (m_specify.dtlh & DTLH_NPOL)
|
||||
{
|
||||
// non-polled mode
|
||||
m_buf[m_buf_count++] = IST_SEN | m_ua;
|
||||
m_state = COMPLETE;
|
||||
|
||||
m_state_timer->adjust(attotime::from_ticks(m_specify.stp((pcn > m_pcn[m_ua]) ? pcn - m_pcn[m_ua] : m_pcn[m_ua] - pcn), clock()));
|
||||
m_pcn[m_ua] = pcn;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = SEEK_POLLED0;
|
||||
m_state_timer->adjust(execute, pcn);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x7: // format
|
||||
LOGMASKED(LOG_COMMAND, "format (not emulated)\n");
|
||||
break;
|
||||
case 0x8: // verify id
|
||||
LOGMASKED(LOG_COMMAND, "verify id (not emulated)\n");
|
||||
break;
|
||||
case 0x9: // read id
|
||||
LOGMASKED(LOG_COMMAND, "read id (not emulated)\n");
|
||||
break;
|
||||
case 0xa: // read diagnostic
|
||||
LOGMASKED(LOG_COMMAND, "read diagnostic (not emulated)\n");
|
||||
break;
|
||||
case 0xb: // read data
|
||||
if (m_specify.mode & SM_SSEC)
|
||||
{
|
||||
m_transfer.phn = m_buf[0];
|
||||
m_transfer.lcnh = m_buf[1];
|
||||
m_transfer.lcnl = m_buf[2];
|
||||
m_transfer.lhn = m_buf[3];
|
||||
m_transfer.lsn = m_buf[4];
|
||||
m_transfer.scnt = m_buf[5];
|
||||
|
||||
m_buf_index = 0;
|
||||
m_buf_count = 0;
|
||||
|
||||
LOGMASKED(LOG_COMMAND, "read data phn 0x%02x lcn 0x%04x lhn 0x%02x lsn 0x%02x scnt 0x%02x\n",
|
||||
m_transfer.phn, m_transfer.lcn(), m_transfer.lhn, m_transfer.lsn, m_transfer.scnt);
|
||||
|
||||
m_state = EXECUTE_READ;
|
||||
m_state_timer->adjust(execute);
|
||||
}
|
||||
break;
|
||||
case 0xc: // check
|
||||
LOGMASKED(LOG_COMMAND, "check (not emulated)\n");
|
||||
break;
|
||||
case 0xd: // scan
|
||||
LOGMASKED(LOG_COMMAND, "scan (not emulated)\n");
|
||||
break;
|
||||
case 0xe: // verify data
|
||||
LOGMASKED(LOG_COMMAND, "verify data (not emulated)\n");
|
||||
break;
|
||||
case 0xf: // write data
|
||||
if (m_specify.mode & SM_SSEC)
|
||||
{
|
||||
m_transfer.phn = m_buf[0];
|
||||
m_transfer.lcnh = m_buf[1];
|
||||
m_transfer.lcnl = m_buf[2];
|
||||
m_transfer.lhn = m_buf[3];
|
||||
m_transfer.lsn = m_buf[4];
|
||||
m_transfer.scnt = m_buf[5];
|
||||
|
||||
m_buf_index = 0;
|
||||
m_buf_count = 0;
|
||||
|
||||
LOGMASKED(LOG_COMMAND, "write data phn 0x%02x lcn 0x%04x lhn 0x%02x lsn 0x%02x scnt 0x%02x\n",
|
||||
m_transfer.phn, m_transfer.lcn(), m_transfer.lhn, m_transfer.lsn, m_transfer.scnt);
|
||||
|
||||
m_state = EXECUTE_WRITE;
|
||||
set_dreq(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 mask = (S_CEH | S_CEL | S_SRQ);
|
||||
|
||||
LOGMASKED(LOG_COMMAND, "auxiliary command%s%s%s%s\n",
|
||||
BIT(data, 0) ? ", chip reset" : "",
|
||||
BIT(data, 1) ? ", clear data" : "",
|
||||
BIT(data, 2) ? ", clear srq" : "",
|
||||
BIT(data, 3) ? ", clear ce" : "");
|
||||
|
||||
if (BIT(data, 0))
|
||||
reset();
|
||||
if (BIT(data, 1))
|
||||
{
|
||||
m_buf_index = 0;
|
||||
m_buf_count = 0;
|
||||
}
|
||||
if (BIT(data, 2))
|
||||
mask &= ~S_SRQ;
|
||||
if (BIT(data, 3))
|
||||
m_status &= ~(S_CEH | S_CEL);
|
||||
|
||||
set_int(m_status & mask);
|
||||
}
|
||||
}
|
||||
|
||||
void upd7261_device::state_timer(s32 param)
|
||||
{
|
||||
// step state machine
|
||||
attotime const delay = state_step(param);
|
||||
|
||||
// check for data stall
|
||||
if (delay.is_never())
|
||||
return;
|
||||
|
||||
// repeat until idle
|
||||
if (m_state != IDLE)
|
||||
m_state_timer->adjust(delay);
|
||||
}
|
||||
|
||||
attotime upd7261_device::state_step(s32 param)
|
||||
{
|
||||
attotime delay = attotime::zero;
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case IDLE:
|
||||
break;
|
||||
|
||||
case EXECUTE_READ:
|
||||
// check unit address is valid
|
||||
if (!m_drive[m_ua] && m_drive[m_ua]->exists())
|
||||
m_est |= EST_NR;
|
||||
|
||||
if (m_transfer.scnt && !m_est)
|
||||
{
|
||||
// HACK: The MG-1 has an additional external head select bit, and
|
||||
// expects the controller to report "no data" when the sector to be
|
||||
// transferred can't be found on the track. This hack uses a fake
|
||||
// external head register to check whether the current head matches
|
||||
// before reading the disk.
|
||||
if (m_transfer.lhn == ((m_head & ~7) | (m_transfer.lhn & 7)))
|
||||
{
|
||||
// read a sector
|
||||
harddisk_image_device &hid(*m_drive[m_ua]);
|
||||
hard_disk_file::info const &i = hid.get_info();
|
||||
|
||||
u32 const lba = ((m_transfer.lcn() * i.heads) + m_transfer.lhn) * i.sectors + m_transfer.lsn;
|
||||
|
||||
hid.read(lba, m_buf.get());
|
||||
m_buf_index = 0;
|
||||
|
||||
m_transfer.scnt--;
|
||||
|
||||
if (m_transfer.lsn++ == m_specify.esn)
|
||||
{
|
||||
m_transfer.lsn = 0;
|
||||
|
||||
if (m_transfer.lhn++ == m_specify.etn)
|
||||
{
|
||||
m_transfer.lhn = 0;
|
||||
m_transfer.lcnl++;
|
||||
if (m_transfer.lcnl == 0)
|
||||
m_transfer.lcnh++;
|
||||
|
||||
if (m_transfer.scnt)
|
||||
m_est |= EST_ENC;
|
||||
}
|
||||
}
|
||||
|
||||
delay = attotime::never;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_est = EST_ND;
|
||||
m_state = RESULTS_bcdef;
|
||||
m_buf_index = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = RESULTS_bcdef;
|
||||
m_buf_index = 0;
|
||||
}
|
||||
|
||||
set_dreq(m_state == EXECUTE_READ);
|
||||
break;
|
||||
|
||||
case EXECUTE_WRITE:
|
||||
// check unit address is valid
|
||||
if (!m_drive[m_ua] && m_drive[m_ua]->exists())
|
||||
m_est |= EST_NR;
|
||||
|
||||
if (m_transfer.scnt && !m_est)
|
||||
{
|
||||
// HACK: as above for reading
|
||||
if (m_transfer.lhn == ((m_head & ~7) | (m_transfer.lhn & 7)))
|
||||
{
|
||||
// write a sector
|
||||
harddisk_image_device &hid(*m_drive[m_ua]);
|
||||
hard_disk_file::info const &i = hid.get_info();
|
||||
|
||||
u32 const lba = ((m_transfer.lcn() * i.heads) + m_transfer.lhn) * i.sectors + m_transfer.lsn;
|
||||
|
||||
hid.write(lba, m_buf.get());
|
||||
m_buf_index = 0;
|
||||
|
||||
m_transfer.scnt--;
|
||||
|
||||
if (m_transfer.lsn++ == m_specify.esn)
|
||||
{
|
||||
m_transfer.lsn = 0;
|
||||
|
||||
if (m_transfer.lhn++ == m_specify.etn)
|
||||
{
|
||||
m_transfer.lhn = 0;
|
||||
m_transfer.lcnl++;
|
||||
if (m_transfer.lcnl == 0)
|
||||
m_transfer.lcnh++;
|
||||
|
||||
if (m_transfer.scnt)
|
||||
{
|
||||
m_est |= EST_ENC;
|
||||
m_state = RESULTS_bcdef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_transfer.scnt)
|
||||
m_state = RESULTS_bcdef;
|
||||
|
||||
if (m_state == EXECUTE_WRITE)
|
||||
delay = attotime::never;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_est = EST_ND;
|
||||
m_state = RESULTS_bcdef;
|
||||
m_buf_index = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = RESULTS_bcdef;
|
||||
m_buf_index = 0;
|
||||
}
|
||||
|
||||
set_dreq(m_state == EXECUTE_WRITE);
|
||||
break;
|
||||
|
||||
case SEEK_POLLED0:
|
||||
m_status &= ~S_CB;
|
||||
m_status |= S_CEH;
|
||||
m_state = SEEK_POLLED1;
|
||||
|
||||
delay = attotime::from_ticks(m_specify.stp(std::abs(m_pcn[m_ua] - param)), clock());
|
||||
m_pcn[m_ua] = param;
|
||||
|
||||
set_int(true);
|
||||
break;
|
||||
|
||||
case SEEK_POLLED1:
|
||||
m_ist |= IST_SEN | m_ua;
|
||||
m_status |= S_SRQ;
|
||||
m_state = IDLE;
|
||||
|
||||
set_int(true);
|
||||
break;
|
||||
|
||||
case RESULTS_4:
|
||||
m_buf[m_buf_count++] = 0; // eadh
|
||||
m_buf[m_buf_count++] = 0; // eadl
|
||||
m_buf[m_buf_count++] = 0; // ept1
|
||||
m_buf[m_buf_count++] = 0; // ept2
|
||||
m_buf[m_buf_count++] = 0; // ept3
|
||||
|
||||
m_state = COMPLETE;
|
||||
break;
|
||||
|
||||
case RESULTS_789:
|
||||
m_buf[m_buf_count++] = m_est;
|
||||
m_buf[m_buf_count++] = m_transfer.scnt;
|
||||
|
||||
m_state = COMPLETE;
|
||||
break;
|
||||
|
||||
case RESULTS_bcdef:
|
||||
m_buf[m_buf_count++] = m_est;
|
||||
m_buf[m_buf_count++] = m_transfer.phn;
|
||||
// (flag)
|
||||
m_buf[m_buf_count++] = m_transfer.lcnh;
|
||||
m_buf[m_buf_count++] = m_transfer.lcnl;
|
||||
m_buf[m_buf_count++] = m_transfer.lhn;
|
||||
m_buf[m_buf_count++] = m_transfer.lsn;
|
||||
m_buf[m_buf_count++] = m_transfer.scnt;
|
||||
|
||||
m_state = COMPLETE;
|
||||
break;
|
||||
|
||||
case COMPLETE:
|
||||
if (m_est)
|
||||
m_status |= S_CEL;
|
||||
else
|
||||
m_status |= S_CEH;
|
||||
m_status &= ~S_CB;
|
||||
|
||||
set_int(true);
|
||||
|
||||
m_state = IDLE;
|
||||
break;
|
||||
|
||||
case ERROR:
|
||||
m_status |= S_CEH | S_CEL;
|
||||
m_status &= ~S_CB;
|
||||
|
||||
set_int(true);
|
||||
|
||||
m_state = IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
return delay;
|
||||
}
|
||||
|
||||
u16 upd7261_device::specify::dtl() const
|
||||
{
|
||||
return u16(dtlh & DTLH_DTLH) << 8 | dtll;
|
||||
}
|
||||
|
||||
u16 upd7261_device::transfer::lcn() const
|
||||
{
|
||||
return u16(lcnh) << 8 | lcnl;
|
||||
}
|
||||
|
||||
unsigned upd7261_device::specify::stp(unsigned cylinders) const
|
||||
{
|
||||
/*
|
||||
* Datasheet formula for ST506 interface stepping is given as:
|
||||
*
|
||||
* (16 - stp) * 2110 * tCY
|
||||
*
|
||||
* Example for a 10MHz clock gives a range of stepping values from
|
||||
* 2.11ms to 33.76ms; formula seems incorrect by a factor of 10?
|
||||
*/
|
||||
return (16 - (mode & SM_STP)) * 21100 * cylinders;
|
||||
}
|
104
src/devices/machine/upd7261.h
Normal file
104
src/devices/machine/upd7261.h
Normal file
@ -0,0 +1,104 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Patrick Mackinlay
|
||||
|
||||
#ifndef MAME_MACHINE_UPD7261_H
|
||||
#define MAME_MACHINE_UPD7261_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imagedev/harddriv.h"
|
||||
|
||||
class upd7261_device
|
||||
: public device_t
|
||||
{
|
||||
public:
|
||||
upd7261_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
|
||||
|
||||
// output lines
|
||||
auto out_dreq() { return m_dreq.bind(); }
|
||||
auto out_int() { return m_int.bind(); }
|
||||
|
||||
// input lines
|
||||
void tc_w(int state);
|
||||
void head_w(u8 data) { m_head = data; }
|
||||
|
||||
void map(address_map &map);
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
void set_dreq(int state);
|
||||
void set_int(bool state);
|
||||
|
||||
u8 data_r();
|
||||
u8 status_r();
|
||||
void data_w(u8 data);
|
||||
void command_w(u8 data);
|
||||
|
||||
void state_timer(s32 param);
|
||||
attotime state_step(s32 param);
|
||||
|
||||
private:
|
||||
optional_device_array<harddisk_image_device, 8> m_drive;
|
||||
|
||||
devcb_write_line m_dreq;
|
||||
devcb_write_line m_int;
|
||||
|
||||
emu_timer *m_state_timer;
|
||||
|
||||
u32 m_state;
|
||||
u8 m_head; // head number (extended)
|
||||
|
||||
u8 m_status; // status register
|
||||
u8 m_est; // error status byte
|
||||
u8 m_ist; // interrupt status byte
|
||||
u8 m_ua; // unit address
|
||||
u16 m_pcn[4]; // physical cylinder number
|
||||
|
||||
struct specify
|
||||
{
|
||||
// data transfer length
|
||||
u16 dtl() const;
|
||||
|
||||
// step time in ticks
|
||||
unsigned stp(unsigned cylinders) const;
|
||||
|
||||
u8 mode; // operation mode
|
||||
u8 dtlh; // data transfer length (high byte)
|
||||
u8 dtll; // data transfer length (low byte)
|
||||
u8 etn; // ending track number
|
||||
u8 esn; // ending sector number
|
||||
u8 gpl2; // gap length 2
|
||||
u8 rwch; // reduced write current cylinder (high byte)
|
||||
u8 rwcl; // reduced write current cylinder (low byte)
|
||||
}
|
||||
m_specify;
|
||||
struct transfer
|
||||
{
|
||||
// logical cylinder number
|
||||
u16 lcn() const;
|
||||
|
||||
u8 phn; // physical head number
|
||||
u8 lcnh; // logical cylinder number (high byte)
|
||||
u8 lcnl; // logical cylinder number (low byte)
|
||||
u8 lhn; // logical head number
|
||||
u8 lsn; // logical sector number
|
||||
u8 scnt; // sector count
|
||||
}
|
||||
m_transfer;
|
||||
|
||||
// data buffer
|
||||
u32 m_buf_index;
|
||||
u32 m_buf_count;
|
||||
std::unique_ptr<u8[]> m_buf;
|
||||
|
||||
// i/o line state
|
||||
bool m_dreq_state;
|
||||
bool m_int_state;
|
||||
bool m_tc_state;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(UPD7261, upd7261_device)
|
||||
|
||||
#endif // MAME_MACHINE_UPD7261_H
|
Loading…
Reference in New Issue
Block a user