mirror of
https://github.com/holub/mame
synced 2025-10-04 16:34:53 +03:00
WD1000: new hard disk controller
Used by the SWTPC09 DMAF3 and it boots UniFLEX on a disk image, and perhaps some other machines can leverage this.
This commit is contained in:
parent
790b0fc1c5
commit
5c53673c5e
@ -3107,6 +3107,18 @@ if (MACHINES["WD_FDC"]~=null) then
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/wd1000.h,MACHINES["WD1000"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (MACHINES["WD1000"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/machine/wd1000.cpp",
|
||||
MAME_DIR .. "src/devices/machine/wd1000.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/wd1010.h,MACHINES["WD1010"] = true
|
||||
|
622
src/devices/machine/wd1000.cpp
Normal file
622
src/devices/machine/wd1000.cpp
Normal file
@ -0,0 +1,622 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:68bit
|
||||
/***************************************************************************
|
||||
|
||||
Western Digital WD1000 Winchester Disk Controller
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "machine/wd1000.h"
|
||||
|
||||
//**************************************************************************
|
||||
// DEVICE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
DEFINE_DEVICE_TYPE(WD1000, wd1000_device, "wd1000", "Western Digital WD1000 Winchester Disk Controller")
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// LIVE DEVICE
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// wd1000_device - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
wd1000_device::wd1000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, WD1000, tag, owner, clock)
|
||||
, m_intrq_cb(*this)
|
||||
, m_drq_cb(*this)
|
||||
, m_sector_base(0)
|
||||
, m_buffer_index(0)
|
||||
, m_buffer_end(0)
|
||||
, m_intrq(0)
|
||||
, m_drq(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)
|
||||
{
|
||||
}
|
||||
|
||||
void wd1000_device::set_sector_base(uint32_t base)
|
||||
{
|
||||
m_sector_base = base;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void wd1000_device::device_start()
|
||||
{
|
||||
// Resolve callbacks
|
||||
m_intrq_cb.resolve();
|
||||
m_drq_cb.resolve();
|
||||
|
||||
// Allocate timers
|
||||
m_seek_timer = timer_alloc(TIMER_SEEK);
|
||||
m_drq_timer = timer_alloc(TIMER_DRQ);
|
||||
|
||||
// Empty buffer.
|
||||
m_buffer_index = 0;
|
||||
|
||||
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].cylinder = 0;
|
||||
}
|
||||
|
||||
// Initialize the status as ready if the initial drive exists,
|
||||
// and assume it has been restored so seek is complete.
|
||||
if (m_drives[drive()].drive->exists())
|
||||
m_status |= S_RDY | S_SC;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_reset - device-specific reset
|
||||
//-------------------------------------------------
|
||||
|
||||
void wd1000_device::device_reset()
|
||||
{
|
||||
m_buffer_index = 0;
|
||||
m_sdh = 0x20;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
m_drives[i].cylinder = 0;
|
||||
|
||||
if (m_drives[drive()].drive->exists())
|
||||
m_status |= S_RDY | S_SC;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_timer - device-specific timer
|
||||
//-------------------------------------------------
|
||||
|
||||
void wd1000_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
|
||||
{
|
||||
switch (tid)
|
||||
{
|
||||
case TIMER_SEEK:
|
||||
|
||||
m_drives[drive()].cylinder = param;
|
||||
m_status |= S_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:
|
||||
cmd_write_sector();
|
||||
break;
|
||||
|
||||
case CMD_WRITE_FORMAT:
|
||||
cmd_format_sector();
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TIMER_DRQ:
|
||||
set_drq();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void wd1000_device::set_error(int error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
m_error |= error;
|
||||
m_status |= S_ERR;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_error = 0;
|
||||
m_status &= ~S_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
void wd1000_device::set_intrq(int state)
|
||||
{
|
||||
if (m_intrq == 0 && state == 1)
|
||||
{
|
||||
m_intrq = 1;
|
||||
m_intrq_cb(1);
|
||||
}
|
||||
else if (m_intrq == 1 && state == 0)
|
||||
{
|
||||
m_intrq = 0;
|
||||
m_intrq_cb(0);
|
||||
}
|
||||
}
|
||||
|
||||
void wd1000_device::set_drq()
|
||||
{
|
||||
if ((m_status & S_DRQ) == 0)
|
||||
{
|
||||
m_status |= S_DRQ;
|
||||
if (!m_drq_cb.isnull())
|
||||
m_drq_cb(true);
|
||||
}
|
||||
}
|
||||
|
||||
void wd1000_device::drop_drq()
|
||||
{
|
||||
if (m_status & S_DRQ)
|
||||
{
|
||||
m_status &= ~S_DRQ;
|
||||
if (!m_drq_cb.isnull())
|
||||
m_drq_cb(false);
|
||||
}
|
||||
}
|
||||
|
||||
attotime wd1000_device::get_stepping_rate()
|
||||
{
|
||||
if (m_stepping_rate)
|
||||
return attotime::from_usec(500 * m_stepping_rate);
|
||||
else
|
||||
return attotime::from_usec(10);
|
||||
}
|
||||
|
||||
void wd1000_device::start_command()
|
||||
{
|
||||
m_status |= S_BSY;
|
||||
set_error(0);
|
||||
|
||||
switch (m_command >> 4)
|
||||
{
|
||||
case CMD_RESTORE:
|
||||
m_stepping_rate = m_command & 0x0f;
|
||||
break;
|
||||
case CMD_READ_SECTOR:
|
||||
break;
|
||||
case CMD_WRITE_SECTOR:
|
||||
break;
|
||||
case CMD_WRITE_FORMAT:
|
||||
break;
|
||||
case CMD_SEEK:
|
||||
m_stepping_rate = m_command & 0x0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void wd1000_device::end_command()
|
||||
{
|
||||
m_status &= ~S_BSY;
|
||||
set_intrq(1);
|
||||
}
|
||||
|
||||
int wd1000_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;
|
||||
|
||||
if (m_cylinder > info->cylinders)
|
||||
{
|
||||
logerror("%s: Unexpected cylinder %d for range 0 to %d\n", machine().describe_context(), m_cylinder, info->cylinders - 1);
|
||||
}
|
||||
|
||||
if (head() >= info->heads)
|
||||
{
|
||||
logerror("%s: Unexpected head %d for range 0 to %d\n", machine().describe_context(), head(), info->heads - 1);
|
||||
}
|
||||
|
||||
int16_t sector = m_sector_number - m_sector_base;
|
||||
|
||||
if (sector < 0 || sector >= info->sectors)
|
||||
{
|
||||
logerror("%s: Unexpected sector number %d for range %d to %d\n", machine().describe_context(), m_sector_number, m_sector_base, info->sectors + m_sector_base);
|
||||
}
|
||||
|
||||
if (sector_bytes() != info->sectorbytes)
|
||||
{
|
||||
logerror("%s: Unexpected sector bytes %d, expected %d\n", machine().describe_context(), sector_bytes(), info->sectorbytes);
|
||||
}
|
||||
|
||||
lbasector = m_cylinder;
|
||||
lbasector *= info->heads;
|
||||
lbasector += head();
|
||||
lbasector *= info->sectors;
|
||||
lbasector += sector;
|
||||
|
||||
return lbasector;
|
||||
}
|
||||
|
||||
uint8_t wd1000_device::data_r()
|
||||
{
|
||||
uint8_t data = 0x00;
|
||||
|
||||
if (machine().side_effects_disabled())
|
||||
return data;
|
||||
|
||||
drop_drq();
|
||||
|
||||
if (m_buffer_index >= m_buffer_end)
|
||||
{
|
||||
// Transfer has already completed.
|
||||
logerror("%s: Unexpected buffer read at %d beyond tail %d\n", machine().describe_context(), m_buffer_index, m_buffer_end);
|
||||
return data;
|
||||
}
|
||||
|
||||
data = m_buffer[m_buffer_index++];
|
||||
|
||||
if (m_buffer_index == m_buffer_end)
|
||||
{
|
||||
// Tranfer completed.
|
||||
if ((m_command >> 4) == CMD_READ_SECTOR)
|
||||
{
|
||||
uint8_t dma = BIT(m_command, 3);
|
||||
|
||||
if (dma)
|
||||
{
|
||||
set_intrq(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logerror("%s: Unexpected buffer read transfer for command %02x\n", machine().describe_context(), m_command);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Continue the transfer. A delay is implemented here to avoid
|
||||
// recursion in the DRQ handler.
|
||||
m_drq_timer->adjust(attotime::from_usec(1));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
READ8_MEMBER( wd1000_device::read )
|
||||
{
|
||||
if ((m_status & S_BSY) && offset != 7)
|
||||
{
|
||||
logerror("%s Unexpected register %d read while busy\n", machine().describe_context(), offset & 0x07);
|
||||
return m_status;
|
||||
}
|
||||
|
||||
switch (offset & 0x07)
|
||||
{
|
||||
case 0:
|
||||
// Data register.
|
||||
return data_r();
|
||||
|
||||
case 1:
|
||||
// Error register:
|
||||
// bit 7 bad block detect
|
||||
// bit 6 CRC error, data field
|
||||
// bit 5 CRC error, ID field
|
||||
// bit 4 ID not found
|
||||
// bit 3 unused
|
||||
// bit 2 Aborted Command
|
||||
// bit 1 TR000 (track zero) error
|
||||
// bit 0 DAM not found
|
||||
// The error register is only valid if the error bit in the
|
||||
// status register is set
|
||||
return m_error;
|
||||
|
||||
case 2:
|
||||
// Sector count. This is only used in the format command where
|
||||
// it is decremented to zero.
|
||||
return m_sector_count;
|
||||
|
||||
case 3:
|
||||
// Sector number.
|
||||
return m_sector_number;
|
||||
|
||||
case 4:
|
||||
// Cylinder low byte C0-C7
|
||||
// This read clears DRQ. This is intended for the handling
|
||||
// aborted sector reads that leave DRQ asserted.
|
||||
if (!machine().side_effects_disabled())
|
||||
{
|
||||
set_intrq(0);
|
||||
drop_drq();
|
||||
}
|
||||
return m_cylinder & 0xff;
|
||||
|
||||
case 5:
|
||||
// Cylinder high byte C8-C9
|
||||
return m_cylinder >> 8;
|
||||
|
||||
case 6:
|
||||
// Size / head / drive
|
||||
return m_sdh;
|
||||
|
||||
case 7:
|
||||
// Status.
|
||||
// bit 7 Busy
|
||||
// bit 6 Ready
|
||||
// bit 5 Write fault
|
||||
// bit 4 Seek complete
|
||||
// bit 3 Data request (DRQ)
|
||||
// bit 2 -
|
||||
// bit 1 -
|
||||
// bit 0 Error
|
||||
// return 'ready' + 'seek complete'
|
||||
// If the busy bit is set then no other bits are valid!!!
|
||||
//
|
||||
// Reading the status register clears the interrupt.
|
||||
//
|
||||
// TODO should reading the status register while busy reset
|
||||
// the interrupt?
|
||||
if (!machine().side_effects_disabled())
|
||||
set_intrq(0);
|
||||
|
||||
return m_status;
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void wd1000_device::data_w(uint8_t data)
|
||||
{
|
||||
drop_drq();
|
||||
|
||||
if (m_buffer_index >= m_buffer_end)
|
||||
{
|
||||
// Transfer has already completed.
|
||||
logerror("%s: Unexpected buffer write at %d beyond tail %d\n", machine().describe_context(), m_buffer_index, m_buffer_end);
|
||||
return;
|
||||
}
|
||||
|
||||
m_buffer[m_buffer_index++] = data;
|
||||
|
||||
if (m_buffer_index == m_buffer_end)
|
||||
{
|
||||
// Tranfer completed.
|
||||
if ((m_command >> 4) == CMD_WRITE_SECTOR ||
|
||||
(m_command >> 4) == CMD_WRITE_FORMAT)
|
||||
{
|
||||
m_status |= S_BSY;
|
||||
set_error(0);
|
||||
// Implied seek
|
||||
m_status &= ~S_SC;
|
||||
int amount = abs(m_drives[drive()].cylinder - m_cylinder);
|
||||
int target = m_cylinder;
|
||||
m_seek_timer->adjust(get_stepping_rate() * amount, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
logerror("%s: Unexpected buffer write transfer for command %02x\n", machine().describe_context(), m_command);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Continue the transfer
|
||||
m_drq_timer->adjust(attotime::from_usec(1));
|
||||
}
|
||||
}
|
||||
|
||||
WRITE8_MEMBER( wd1000_device::write )
|
||||
{
|
||||
switch (offset & 0x07)
|
||||
{
|
||||
case 0:
|
||||
// Data register
|
||||
data_w(data);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Write precomp
|
||||
m_precomp = data;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Sector Count
|
||||
m_sector_count = data;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Sector Number
|
||||
m_sector_number = data;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// Cylinder Low
|
||||
m_cylinder = (m_cylinder & 0xff00) | (data << 0);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Cylinder High
|
||||
m_cylinder = (m_cylinder & 0x00ff) | (data << 8);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
{
|
||||
// Size / drive / head
|
||||
// bit 7 : Honour error correction (WD-1001 only?)
|
||||
// bit 6,5 : sector size (0: 256, 1: 512, 3: 128)
|
||||
// bit 4,3 : drive select (0,1,2,3)
|
||||
// bit 2,1,0: head select (0-7)
|
||||
uint8_t ecc = BIT(data, 7);
|
||||
uint8_t sec_size = (data & 0x60) >> 5;
|
||||
uint8_t drive = (data & 0x18) >> 3;
|
||||
uint8_t head = data & 0x7;
|
||||
m_sdh = data;
|
||||
|
||||
// Update the drive ready flag in the status register which
|
||||
// indicates the ready state for the selected drive.
|
||||
// TODO should this also set the SC flag?
|
||||
if (m_drives[drive].drive->exists())
|
||||
m_status |= S_RDY;
|
||||
else
|
||||
m_status &= ~S_RDY;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 7:
|
||||
// Command register
|
||||
// Load while the controller is not busy.
|
||||
// Task file must have been loaded??
|
||||
// Not if 'Seek complete' or 'Ready' are false, or 'Write fault' is true.
|
||||
|
||||
if (m_status & S_BSY)
|
||||
{
|
||||
logerror("%s: Unexpected command %02x issued when already busy with %02x\n", machine().describe_context(), data, m_command);
|
||||
// TODO should it be ignored?
|
||||
//return;
|
||||
}
|
||||
|
||||
// Interrupts are cleared and the error bit is reset
|
||||
// on receipt of a new command.
|
||||
set_intrq(0);
|
||||
set_error(0);
|
||||
|
||||
if (!(m_status & S_RDY))
|
||||
{
|
||||
logerror("%s: Unexpected command %02x when not ready\n", machine().describe_context(), m_command);
|
||||
set_error(ERR_AC);
|
||||
end_command();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_command = data;
|
||||
|
||||
switch (m_command >> 4)
|
||||
{
|
||||
case CMD_RESTORE:
|
||||
{
|
||||
start_command();
|
||||
// Schedule an implied seek.
|
||||
m_status &= ~S_SC;
|
||||
int amount = m_drives[drive()].cylinder;
|
||||
int target = 0;
|
||||
m_seek_timer->adjust(get_stepping_rate() * amount, target);
|
||||
break;
|
||||
}
|
||||
|
||||
case CMD_SEEK:
|
||||
case CMD_READ_SECTOR:
|
||||
{
|
||||
start_command();
|
||||
int amount = abs(m_drives[drive()].cylinder - m_cylinder);
|
||||
int target = m_cylinder;
|
||||
m_seek_timer->adjust(get_stepping_rate() * amount, target);
|
||||
break;
|
||||
}
|
||||
|
||||
case CMD_WRITE_SECTOR:
|
||||
case CMD_WRITE_FORMAT:
|
||||
// These commands do not perform an implied
|
||||
// seek until all the data has been
|
||||
// transferred, so just start the transfer
|
||||
// here.
|
||||
m_buffer_index = 0;
|
||||
m_buffer_end = 512;
|
||||
set_drq();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void wd1000_device::cmd_restore()
|
||||
{
|
||||
end_command();
|
||||
}
|
||||
|
||||
void wd1000_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);
|
||||
uint8_t dma = BIT(m_command, 3);
|
||||
|
||||
hard_disk_read(file, get_lbasector(), m_buffer);
|
||||
|
||||
m_buffer_index = 0;
|
||||
m_buffer_end = 512;
|
||||
|
||||
m_status &= ~S_BSY;
|
||||
|
||||
set_drq();
|
||||
|
||||
if (!dma)
|
||||
{
|
||||
// Interrupt now, rather than at the end of the DMA transfer.
|
||||
set_intrq(1);
|
||||
}
|
||||
}
|
||||
|
||||
void wd1000_device::cmd_write_sector()
|
||||
{
|
||||
hard_disk_file *file = m_drives[drive()].drive->get_hard_disk_file();
|
||||
|
||||
if (m_buffer_index != sector_bytes())
|
||||
{
|
||||
logerror("%s: Unexpected unfilled buffer on write, only %d or %d bytes filled\n", machine().describe_context(), m_buffer_index, sector_bytes());
|
||||
}
|
||||
|
||||
hard_disk_write(file, get_lbasector(), m_buffer);
|
||||
|
||||
end_command();
|
||||
}
|
||||
|
||||
void wd1000_device::cmd_format_sector()
|
||||
{
|
||||
hard_disk_file *file = m_drives[drive()].drive->get_hard_disk_file();
|
||||
uint8_t buffer[512];
|
||||
|
||||
// The m_buffer appears to be loaded with an interleave table which is
|
||||
// not used here. The sectors are zero filled.
|
||||
|
||||
for (int i = 0; i < m_sector_count; i++)
|
||||
{
|
||||
bzero(buffer, sizeof(buffer));
|
||||
hard_disk_write(file, get_lbasector(), buffer);
|
||||
}
|
||||
|
||||
m_sector_count = 0;
|
||||
|
||||
end_command();
|
||||
}
|
||||
|
||||
void wd1000_device::cmd_seek()
|
||||
{
|
||||
end_command();
|
||||
}
|
143
src/devices/machine/wd1000.h
Normal file
143
src/devices/machine/wd1000.h
Normal file
@ -0,0 +1,143 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:68bit
|
||||
/***************************************************************************
|
||||
|
||||
Western Digital WD1000-05 Winchester Disk Controller
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MAME_MACHINE_WD1000_H
|
||||
#define MAME_MACHINE_WD1000_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imagedev/harddriv.h"
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
// ======================> wd1000_device
|
||||
|
||||
class wd1000_device : public device_t
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
wd1000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
auto intrq_wr_callback() { return m_intrq_cb.bind(); }
|
||||
auto drq_wr_callback() { return m_drq_cb.bind(); }
|
||||
|
||||
uint8_t data_r();
|
||||
void data_w(uint8_t val);
|
||||
void set_sector_base(uint32_t base);
|
||||
|
||||
DECLARE_READ8_MEMBER(read);
|
||||
DECLARE_WRITE8_MEMBER(write);
|
||||
|
||||
DECLARE_READ_LINE_MEMBER(intrq_r);
|
||||
DECLARE_READ_LINE_MEMBER(drq_r);
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
S_ERR = 0x01, // Error
|
||||
S_DRQ = 0x08, // Data request
|
||||
S_SC = 0x10, // Seek complete
|
||||
S_WF = 0x20, // Write fault
|
||||
S_RDY = 0x40, // Drive ready
|
||||
S_BSY = 0x80 // Busy
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ERR_DM = 0x01, // data address mark not found
|
||||
ERR_TK = 0x02, // track zero error
|
||||
ERR_AC = 0x04, // aborted command
|
||||
ERR_ID = 0x10, // id not found
|
||||
ERR_CRC = 0x40, // crc error
|
||||
ERR_BB = 0x80 // bad block
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CMD_RESTORE = 1,
|
||||
CMD_READ_SECTOR = 2,
|
||||
CMD_WRITE_SECTOR = 3,
|
||||
CMD_WRITE_FORMAT = 5,
|
||||
CMD_SEEK = 7
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TIMER_SEEK,
|
||||
TIMER_DRQ,
|
||||
};
|
||||
|
||||
void set_error(int error);
|
||||
void set_intrq(int state);
|
||||
void set_drq();
|
||||
void drop_drq();
|
||||
attotime get_stepping_rate();
|
||||
void start_command();
|
||||
void end_command();
|
||||
int get_lbasector();
|
||||
|
||||
int head() { return (m_sdh >> 0) & 0x07; }
|
||||
int drive() { return (m_sdh >> 3) & 0x03; }
|
||||
int sector_bytes()
|
||||
{
|
||||
const int bytes[4] = { 256, 512, 0, 128 };
|
||||
return bytes[(m_sdh >> 5) & 0x03];
|
||||
}
|
||||
|
||||
void cmd_restore();
|
||||
void cmd_read_sector();
|
||||
void cmd_write_sector();
|
||||
void cmd_format_sector();
|
||||
void cmd_seek();
|
||||
|
||||
devcb_write_line m_intrq_cb, m_drq_cb;
|
||||
|
||||
struct
|
||||
{
|
||||
harddisk_image_device *drive;
|
||||
uint16_t cylinder;
|
||||
} m_drives[4];
|
||||
|
||||
uint16_t m_sector_base;
|
||||
|
||||
// Data buffer
|
||||
uint8_t m_buffer[512];
|
||||
uint16_t m_buffer_index;
|
||||
uint16_t m_buffer_end;
|
||||
|
||||
emu_timer *m_seek_timer;
|
||||
emu_timer *m_drq_timer;
|
||||
|
||||
int m_intrq;
|
||||
int m_drq;
|
||||
uint8_t m_stepping_rate;
|
||||
uint8_t m_command;
|
||||
|
||||
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(WD1000, wd1000_device)
|
||||
|
||||
#endif // MAME_MACHINE_WD1000_H
|
Loading…
Reference in New Issue
Block a user