summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Dirk Best <mail@dirk-best.de>2019-02-23 18:41:38 +0100
committer Dirk Best <mail@dirk-best.de>2019-02-23 20:41:50 +0100
commit093d7cbf18de62093ffba6ce34263cf9160ff19b (patch)
tree5ba2572d0384673feeb4327bb6428e103ae6ce6d
parent465d97346a9e230ca46e6d746b1614fb1c418703 (diff)
New device: Western Digital WD1010-05
-rw-r--r--scripts/src/machine.lua12
-rw-r--r--scripts/target/mame/arcade.lua1
-rw-r--r--scripts/target/mame/mess.lua1
-rw-r--r--src/devices/machine/wd1010.cpp583
-rw-r--r--src/devices/machine/wd1010.h143
5 files changed, 740 insertions, 0 deletions
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua
index 600e9ade88a..edf745c67d3 100644
--- a/scripts/src/machine.lua
+++ b/scripts/src/machine.lua
@@ -2914,6 +2914,18 @@ end
---------------------------------------------------
--
+--@src/devices/machine/wd1010.h,MACHINES["WD1010"] = true
+---------------------------------------------------
+
+if (MACHINES["WD1010"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/machine/wd1010.cpp",
+ MAME_DIR .. "src/devices/machine/wd1010.h",
+ }
+end
+
+---------------------------------------------------
+--
--@src/devices/machine/wd11c00_17.h,MACHINES["WD11C00_17"] = true
---------------------------------------------------
diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua
index 5466a7607ea..4cf2e1469c4 100644
--- a/scripts/target/mame/arcade.lua
+++ b/scripts/target/mame/arcade.lua
@@ -606,6 +606,7 @@ MACHINES["UPD765"] = true
MACHINES["FDC_PLL"] = true
MACHINES["V3021"] = true
MACHINES["WD_FDC"] = true
+--MACHINES["WD1010"] = true
MACHINES["WD11C00_17"] = true
MACHINES["WD2010"] = true
MACHINES["WD33C9X"] = true
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index 911844484c2..bc1fcf2c456 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -622,6 +622,7 @@ MACHINES["UPD765"] = true
MACHINES["FDC_PLL"] = true
MACHINES["V3021"] = true
MACHINES["WD_FDC"] = true
+MACHINES["WD1010"] = true
MACHINES["WD11C00_17"] = true
MACHINES["WD2010"] = true
MACHINES["WD33C9X"] = true
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();
+}
diff --git a/src/devices/machine/wd1010.h b/src/devices/machine/wd1010.h
new file mode 100644
index 00000000000..b5db9975693
--- /dev/null
+++ b/src/devices/machine/wd1010.h
@@ -0,0 +1,143 @@
+// license:GPL-2.0+
+// copyright-holders:Dirk Best
+/***************************************************************************
+
+ Western Digital WD1010-05 Winchester Disk Controller
+
+***************************************************************************/
+
+#ifndef MAME_MACHINE_WD1010_H
+#define MAME_MACHINE_WD1010_H
+
+#pragma once
+
+#include "imagedev/harddriv.h"
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> wd1010_device
+
+class wd1010_device : public device_t
+{
+public:
+ // construction/destruction
+ wd1010_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ auto out_intrq_callback() { return m_out_intrq_cb.bind(); }
+ auto out_bcr_callback() { return m_out_bcr_cb.bind(); }
+ auto in_data_callback() { return m_in_data_cb.bind(); }
+ auto out_data_callback() { return m_out_data_cb.bind(); }
+
+ DECLARE_READ8_MEMBER(read);
+ DECLARE_WRITE8_MEMBER(write);
+
+ DECLARE_WRITE_LINE_MEMBER(drdy_w);
+ DECLARE_WRITE_LINE_MEMBER(brdy_w);
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+
+private:
+ enum
+ {
+ STATUS_ERR = 0x01, // error
+ STATUS_CIP = 0x02, // command in progress
+ STATUS_RSV = 0x04, // reserved
+ STATUS_DRQ = 0x08, // data request
+ STATUS_SC = 0x10, // seek complete
+ STATUS_WF = 0x20, // write fault
+ STATUS_RDY = 0x40, // drive ready
+ STATUS_BSY = 0x80 // controller busy
+ };
+
+ enum
+ {
+ ERR_DM = 0x01, // data address mark not found
+ ERR_TK = 0x02, // track zero error
+ ERR_AC = 0x04, // aborted command
+ ERR_RSV1 = 0x08, // reserved, forced to 0
+ ERR_ID = 0x10, // id not found
+ ERR_RSV2 = 0x20, // reserved, forced to 0
+ ERR_CRC = 0x40, // crc error
+ ERR_BB = 0x80 // bad block
+ };
+
+ enum
+ {
+ CMD_RESTORE = 1,
+ CMD_READ_SECTOR = 2,
+ CMD_WRITE_SECTOR = 3,
+ CMD_SCAN_ID = 4,
+ CMD_WRITE_FORMAT = 5,
+ CMD_SEEK = 7
+ };
+
+ enum
+ {
+ TIMER_SEEK,
+ TIMER_DATA
+ };
+
+ void set_error(int error);
+ void set_intrq(int state);
+ attotime get_stepping_rate();
+ void start_command();
+ void end_command();
+ int get_lbasector();
+
+ // extract values from sdh
+ int head() { return (m_sdh >> 0) & 0x07; }
+ int drive() { return (m_sdh >> 3) & 0x03; }
+ int sector_size()
+ {
+ const int S[4] = { 256, 512, 1024, 128 };
+ return S[(m_sdh >> 5) & 0x03];
+ }
+
+ void cmd_restore();
+ void cmd_read_sector();
+ void cmd_write_sector();
+ void cmd_scan_id();
+ void cmd_seek();
+
+ devcb_write_line m_out_intrq_cb;
+ devcb_write_line m_out_bcr_cb;
+ devcb_read8 m_in_data_cb;
+ devcb_write8 m_out_data_cb;
+
+ struct
+ {
+ harddisk_image_device *drive;
+ uint8_t head;
+ uint16_t cylinder;
+ uint8_t sector;
+ } m_drives[4];
+
+ emu_timer *m_seek_timer;
+ emu_timer *m_data_timer;
+
+ int m_intrq;
+ int m_brdy;
+ uint8_t m_stepping_rate;
+ uint8_t m_command;
+
+ // task file registers
+ uint8_t m_error;
+ uint8_t m_precomp;
+ uint8_t m_sector_count;
+ uint8_t m_sector_number;
+ uint16_t m_cylinder;
+ uint8_t m_sdh;
+ uint8_t m_status;
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(WD1010, wd1010_device)
+
+#endif // MAME_MACHINE_WD1010_H