diff options
author | 2019-03-26 11:13:37 +1100 | |
---|---|---|
committer | 2019-03-26 11:13:37 +1100 | |
commit | 97b67170277437131adf6ed4d60139c172529e4f (patch) | |
tree | 7a5cbf608f191075f1612b1af15832c206a3fe2d /src/devices/machine/wd1010.cpp | |
parent | b380514764cf857469bae61c11143a19f79a74c5 (diff) |
(nw) Clean up the mess on master
This effectively reverts b380514764cf857469bae61c11143a19f79a74c5 and
c24473ddff715ecec2e258a6eb38960cf8c8e98e, restoring the state at
598cd5227223c3b04ca31f0dbc1981256d9ea3ff.
Before pushing, please check that what you're about to push is sane.
Check your local commit log and ensure there isn't anything out-of-place
before pushing to mainline. When things like this happen, it wastes
everyone's time. I really don't need this in a week when real work⢠is
busting my balls and I'm behind where I want to be with preparing for
MAME release.
Diffstat (limited to 'src/devices/machine/wd1010.cpp')
-rw-r--r-- | src/devices/machine/wd1010.cpp | 583 |
1 files changed, 583 insertions, 0 deletions
diff --git a/src/devices/machine/wd1010.cpp b/src/devices/machine/wd1010.cpp new file mode 100644 index 00000000000..16442934352 --- /dev/null +++ b/src/devices/machine/wd1010.cpp @@ -0,0 +1,583 @@ +// license:GPL-2.0+ +// copyright-holders:Dirk Best +/*************************************************************************** + + Western Digital WD1010-05 Winchester Disk Controller + +***************************************************************************/ + +#include "emu.h" +#include "wd1010.h" + +//#define LOG_GENERAL (1U << 0) +#define LOG_CMD (1U << 1) +#define LOG_INT (1U << 2) +#define LOG_SEEK (1U << 3) +#define LOG_REGS (1U << 4) +#define LOG_DATA (1U << 5) + +//#define VERBOSE (LOG_CMD | LOG_INT | LOG_SEEK | LOG_REGS | LOG_DATA) +//#define LOG_OUTPUT_STREAM std::cout + +#include "logmacro.h" + +#define LOGCMD(...) LOGMASKED(LOG_CMD, __VA_ARGS__) +#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__) +#define LOGSEEK(...) LOGMASKED(LOG_SEEK, __VA_ARGS__) +#define LOGREGS(...) LOGMASKED(LOG_REGS, __VA_ARGS__) +#define LOGDATA(...) LOGMASKED(LOG_DATA, __VA_ARGS__) + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +DEFINE_DEVICE_TYPE(WD1010, wd1010_device, "wd1010", "Western Digital WD1010-05") + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// wd1010_device - constructor +//------------------------------------------------- + +wd1010_device::wd1010_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, WD1010, tag, owner, clock), + m_out_intrq_cb(*this), + m_out_bcr_cb(*this), + m_in_data_cb(*this), + m_out_data_cb(*this), + m_intrq(0), + m_brdy(0), + m_stepping_rate(0x00), + m_command(0x00), + m_error(0x00), + m_precomp(0x00), + m_sector_count(0x00), + m_sector_number(0x00), + m_cylinder(0x0000), + m_sdh(0x00), + m_status(0x00) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void wd1010_device::device_start() +{ + // get connected drives + for (int i = 0; i < 4; i++) + { + char name[2]; + name[0] = '0' + i; + name[1] = 0; + m_drives[i].drive = subdevice<harddisk_image_device>(name); + m_drives[i].head = 0; + m_drives[i].cylinder = 0; + m_drives[i].sector = 0; + } + + // resolve callbacks + m_out_intrq_cb.resolve_safe(); + m_out_bcr_cb.resolve_safe(); + m_in_data_cb.resolve_safe(0); + m_out_data_cb.resolve_safe(); + + // allocate timer + m_seek_timer = timer_alloc(TIMER_SEEK); + m_data_timer = timer_alloc(TIMER_DATA); + + // register for save states + save_item(NAME(m_intrq)); + save_item(NAME(m_brdy)); + save_item(NAME(m_stepping_rate)); + save_item(NAME(m_command)); + save_item(NAME(m_error)); + save_item(NAME(m_precomp)); + save_item(NAME(m_sector_count)); + save_item(NAME(m_sector_number)); + save_item(NAME(m_cylinder)); + save_item(NAME(m_sdh)); + save_item(NAME(m_status)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void wd1010_device::device_reset() +{ +} + +//------------------------------------------------- +// device_timer - device-specific timer +//------------------------------------------------- + +void wd1010_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr) +{ + switch (tid) + { + case TIMER_SEEK: + + if ((m_command >> 4) != CMD_SCAN_ID) + { + LOGSEEK("Seek complete\n"); + m_drives[drive()].cylinder = param; + m_status |= STATUS_SC; + } + + switch (m_command >> 4) + { + case CMD_RESTORE: + cmd_restore(); + break; + + case CMD_SEEK: + cmd_seek(); + break; + + case CMD_READ_SECTOR: + cmd_read_sector(); + break; + + case CMD_WRITE_SECTOR: + case CMD_WRITE_FORMAT: + cmd_write_sector(); + break; + + case CMD_SCAN_ID: + cmd_scan_id(); + break; + } + + break; + + case TIMER_DATA: + + // check if data is ready or continue waiting + if (m_brdy) + cmd_write_sector(); + else + m_data_timer->adjust(attotime::from_usec(35)); + + break; + } +} + +//------------------------------------------------- +// set_error - set error and adjust status +//------------------------------------------------- + +void wd1010_device::set_error(int error) +{ + if (error) + { + m_error |= error; + m_status |= STATUS_ERR; + } + else + { + m_error = 0; + m_status &= ~STATUS_ERR; + } +} + +//------------------------------------------------- +// set_intrq - set interrupt status +//------------------------------------------------- + +void wd1010_device::set_intrq(int state) +{ + if (m_intrq == 0 && state == 1) + { + LOGINT("INT 1\n"); + m_intrq = 1; + m_out_intrq_cb(1); + } + else if (m_intrq == 1 && state == 0) + { + LOGINT("INT 0\n"); + m_intrq = 0; + m_out_intrq_cb(0); + } +} + +//------------------------------------------------- +// get_stepping_rate - calculate stepping rate +//------------------------------------------------- + +attotime wd1010_device::get_stepping_rate() +{ + if (m_stepping_rate) + return attotime::from_usec(500 * m_stepping_rate); + else + return attotime::from_usec(35); +} + +//------------------------------------------------- +// start_command - executed at the start of every command +//------------------------------------------------- + +void wd1010_device::start_command() +{ + // now busy and command in progress, clear error + m_status |= STATUS_BSY; + m_status |= STATUS_CIP; + set_error(0); + + switch (m_command >> 4) + { + case CMD_RESTORE: + m_stepping_rate = m_command & 0x0f; + LOGCMD("RESTORE\n"); + break; + case CMD_READ_SECTOR: + LOGCMD("READ SECTOR\n"); + break; + case CMD_WRITE_SECTOR: + LOGCMD("WRITE SECTOR\n"); + break; + case CMD_SCAN_ID: + LOGCMD("SCAN ID\n"); + break; + case CMD_WRITE_FORMAT: + LOGCMD("WRITE FORMAT ID\n"); + break; + case CMD_SEEK: + m_stepping_rate = m_command & 0x0f; + LOGCMD("SEEK\n"); + break; + } +} + +//------------------------------------------------- +// end_command - executed at end of every command +//------------------------------------------------- + +void wd1010_device::end_command() +{ + m_out_bcr_cb(1); + m_out_bcr_cb(0); + + m_status &= ~(STATUS_BSY | STATUS_CIP); + + set_intrq(1); +} + +//------------------------------------------------- +// get_lbasector - translate to lba +//------------------------------------------------- + +int wd1010_device::get_lbasector() +{ + hard_disk_file *file = m_drives[drive()].drive->get_hard_disk_file(); + hard_disk_info *info = hard_disk_get_info(file); + int lbasector; + + lbasector = m_cylinder; + lbasector *= info->heads; + lbasector += head(); + lbasector *= info->sectors; + lbasector += m_sector_number; + + return lbasector; +} + + +//************************************************************************** +// INTERFACE +//************************************************************************** + +WRITE_LINE_MEMBER( wd1010_device::drdy_w ) +{ + if (state) + m_status |= STATUS_RDY; + else + m_status &= ~STATUS_RDY; +} + +WRITE_LINE_MEMBER( wd1010_device::brdy_w ) +{ + m_brdy = state; +} + +READ8_MEMBER( wd1010_device::read ) +{ + // if the controller is busy all reads return the status register + if (m_status & STATUS_BSY) + { + LOG("Read while busy, return STATUS: %02x\n", m_status); + return m_status; + } + + switch (offset & 0x07) + { + case 0: + LOGREGS("RD INVALID\n"); + return 0; + + case 1: + LOGREGS("RD ERROR: %02x\n", m_error); + return m_error; + + case 2: + LOGREGS("RD SECTOR COUNT: %02x\n", m_sector_count); + return m_sector_count; + + case 3: + LOGREGS("RD SECTOR NUMBER: %02x\n", m_sector_number); + return m_sector_number; + + case 4: + LOGREGS("RD CYLINDER L: %02x\n", m_cylinder & 0xff); + return m_cylinder & 0xff; + + case 5: + LOGREGS("RD CYLINDER H: %02x\n", m_cylinder >> 8); + return m_cylinder >> 8; + + case 6: + LOGREGS("RD SDH: %02x\n", m_sdh); + return m_sdh; + + case 7: + LOGREGS("RD STATUS: %02x\n", m_status); + + // reading the status register clears the interrupt + set_intrq(0); + + return m_status; + } + + // should never get here + return 0xff; +} + +WRITE8_MEMBER( wd1010_device::write ) +{ + switch (offset & 0x07) + { + case 0: + LOGREGS("WR INVALID: %02x\n", data); + break; + + case 1: + LOGREGS("WR PRECOMP: %02x\n", data); + m_precomp = data; + break; + + case 2: + LOGREGS("WR SECTOR COUNT: %02x\n", data); + m_sector_count = data; + break; + + case 3: + LOGREGS("WR SECTOR NUMBER: %02x\n", data); + m_sector_number = data; + break; + + case 4: + LOGREGS("WR CYLINDER L: %02x\n", data); + m_cylinder = (m_cylinder & 0xff00) | (data << 0); + break; + + case 5: + LOGREGS("WR CYLINDER H: %02x\n", data); + m_cylinder = (m_cylinder & 0x00ff) | (data << 8); + break; + + case 6: + LOGREGS("WR SDH: %02x\n", data); + m_sdh = data; + break; + + case 7: + // writes to the command register are ignored when a command is in progress + if (m_status & STATUS_CIP) + return; + + // writing the command register clears the interrupt + set_intrq(0); + + // check drive status + if (!(m_status & STATUS_RDY)) + { + // selected drive not ready + LOG("--> Drive not ready, aborting\n"); + + set_error(ERR_AC); + end_command(); + } + else + { + m_command = data; + + start_command(); + + // all other command imply a seek + if ((m_command >> 4) != CMD_SCAN_ID) + m_status &= ~STATUS_SC; + + int amount = 0; + int target = 0; + + switch (m_command >> 4) + { + case CMD_RESTORE: + amount = m_drives[drive()].cylinder; + target = 0; + break; + + case CMD_SEEK: + case CMD_READ_SECTOR: + case CMD_WRITE_SECTOR: + case CMD_WRITE_FORMAT: + amount = abs(m_drives[drive()].cylinder - m_cylinder); + target = m_cylinder; + break; + } + + if ((m_command >> 4) != CMD_SCAN_ID) + LOGSEEK("Seeking %d cylinders to %d\n", amount, target); + + m_seek_timer->adjust(get_stepping_rate() * amount, target); + } + + break; + } +} + + +//************************************************************************** +// IMPLEMENTATION +//************************************************************************** + +void wd1010_device::cmd_restore() +{ + LOGCMD("--> RESTORE done\n"); + end_command(); +} + +void wd1010_device::cmd_read_sector() +{ + hard_disk_file *file = m_drives[drive()].drive->get_hard_disk_file(); + hard_disk_info *info = hard_disk_get_info(file); + + m_out_bcr_cb(1); + m_out_bcr_cb(0); + + // verify that we can read + if (head() > info->heads) + { + // out of range + LOG("--> Head out of range, aborting\n"); + + set_error(ERR_AC); + end_command(); + } + else + { + uint8_t buffer[512]; + + while (m_sector_count > 0) + { + LOGDATA("--> Transferring sector to buffer (lba = %08x)\n", get_lbasector()); + + hard_disk_read(file, get_lbasector(), buffer); + + for (int i = 0; i < 512; i++) + m_out_data_cb(buffer[i]); + + m_out_bcr_cb(1); + m_out_bcr_cb(0); + + // save last read head and sector number + m_drives[drive()].head = head(); + m_drives[drive()].sector = m_sector_number; + + if (BIT(m_command, 2)) + { + m_sector_number++; + m_sector_count--; + } + else + break; + } + + end_command(); + } +} + +void wd1010_device::cmd_write_sector() +{ + if (!(m_status & STATUS_DRQ)) + { + LOGDATA("Setting DATA REQUEST\n"); + m_status |= STATUS_DRQ; + m_data_timer->adjust(attotime::from_usec(35)); + return; + } + + if (m_brdy == 0) + { + m_data_timer->adjust(attotime::from_usec(35)); + return; + } + + LOGDATA("Clearing DATA REQUEST\n"); + m_status &= ~STATUS_DRQ; + + hard_disk_file *file = m_drives[drive()].drive->get_hard_disk_file(); + uint8_t buffer[512]; + + while (m_sector_count > 0) + { + if ((m_command >> 4) == CMD_WRITE_FORMAT) + { + // we ignore the format specification and fill everything with 0xe5 + std::fill(std::begin(buffer), std::end(buffer), 0xe5); + } + else + { + // get data for sector from buffer chip + for (int i = 0; i < 512; i++) + buffer[i] = m_in_data_cb(); + } + + hard_disk_write(file, get_lbasector(), buffer); + + // save last read head and sector number + m_drives[drive()].head = head(); + m_drives[drive()].sector = m_sector_number; + + if (BIT(m_command, 2)) + { + m_sector_number++; + m_sector_count--; + } + else + break; + } + + end_command(); +} + +void wd1010_device::cmd_scan_id() +{ + // update head, sector size, sector number and cylinder + m_sdh = (m_sdh & 0xf8) | (m_drives[drive()].head & 0x07); + m_sector_number = m_drives[drive()].sector; + m_cylinder = m_drives[drive()].cylinder; + + end_command(); +} + +void wd1010_device::cmd_seek() +{ + LOGCMD("--> SEEK done\n"); + end_command(); +} |