mirror of
https://github.com/holub/mame
synced 2025-04-26 02:07:14 +03:00
643 lines
16 KiB
C
643 lines
16 KiB
C
/***************************************************************************
|
|
|
|
Generic (PC-style) IDE controller implementation
|
|
|
|
***************************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "idectrl.h"
|
|
#include "debugger.h"
|
|
|
|
/***************************************************************************
|
|
DEBUGGING
|
|
***************************************************************************/
|
|
|
|
#define VERBOSE 0
|
|
#define PRINTF_IDE_COMMANDS 0
|
|
#define PRINTF_IDE_PASSWORD 0
|
|
|
|
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
|
|
|
|
#define LOGPRINT(x) do { if (VERBOSE) logerror x; if (PRINTF_IDE_COMMANDS) mame_printf_debug x; } while (0)
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
CONSTANTS
|
|
***************************************************************************/
|
|
|
|
#define IDE_BANK2_CONFIG_UNK 4
|
|
#define IDE_BANK2_CONFIG_REGISTER 8
|
|
#define IDE_BANK2_CONFIG_DATA 0xc
|
|
|
|
|
|
void ide_controller_device::set_irq(int state)
|
|
{
|
|
if (state == ASSERT_LINE)
|
|
LOG(("IDE interrupt assert\n"));
|
|
else
|
|
LOG(("IDE interrupt clear\n"));
|
|
|
|
/* signal an interrupt */
|
|
m_irq_handler(state);
|
|
}
|
|
|
|
void ide_controller_device::set_dmarq(int state)
|
|
{
|
|
m_dmarq_handler(state);
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( ide_controller_device::irq0_write_line )
|
|
{
|
|
m_irq[0] = state;
|
|
|
|
set_irq(m_irq[0] == ASSERT_LINE || m_irq[1] == ASSERT_LINE);
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( ide_controller_device::irq1_write_line )
|
|
{
|
|
m_irq[1] = state;
|
|
|
|
set_irq(m_irq[0] == ASSERT_LINE || m_irq[1] == ASSERT_LINE);
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( ide_controller_device::dmarq0_write_line )
|
|
{
|
|
m_dmarq[0] = state;
|
|
|
|
set_dmarq(m_dmarq[0] == ASSERT_LINE || m_dmarq[1] == ASSERT_LINE);
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( ide_controller_device::dmarq1_write_line )
|
|
{
|
|
m_dmarq[1] = state;
|
|
|
|
set_dmarq(m_dmarq[0] == ASSERT_LINE || m_dmarq[1] == ASSERT_LINE);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
INITIALIZATION AND RESET
|
|
***************************************************************************/
|
|
|
|
UINT8 *ide_controller_device::ide_get_features(int _drive)
|
|
{
|
|
return m_slot[_drive]->dev()->get_features();
|
|
}
|
|
|
|
void ide_controller_device::ide_set_master_password(int _drive, const UINT8 *password)
|
|
{
|
|
m_slot[_drive]->dev()->m_master_password = password;
|
|
m_slot[_drive]->dev()->m_master_password_enable = (password != NULL);
|
|
}
|
|
|
|
|
|
void ide_controller_device::ide_set_user_password(int _drive, const UINT8 *password)
|
|
{
|
|
m_slot[_drive]->dev()->m_user_password = password;
|
|
m_slot[_drive]->dev()->m_user_password_enable = (password != NULL);
|
|
}
|
|
|
|
/*************************************
|
|
*
|
|
* IDE controller read
|
|
*
|
|
*************************************/
|
|
|
|
UINT16 ide_controller_device::read_dma()
|
|
{
|
|
UINT16 result = 0xffff;
|
|
for (int i = 0; i < 2; i++)
|
|
if (m_slot[i]->dev() != NULL)
|
|
result &= m_slot[i]->dev()->read_dma();
|
|
|
|
// printf( "read_dma %04x\n", result );
|
|
return result;
|
|
}
|
|
|
|
READ16_MEMBER( ide_controller_device::read_cs0 )
|
|
{
|
|
UINT16 result = mem_mask;
|
|
for (int i = 0; i < 2; i++)
|
|
if (m_slot[i]->dev() != NULL)
|
|
result &= m_slot[i]->dev()->read_cs0(space, offset, mem_mask);
|
|
|
|
// printf( "read cs0 %04x %04x %04x\n", offset, result, mem_mask );
|
|
|
|
return result;
|
|
}
|
|
|
|
READ16_MEMBER( ide_controller_device::read_cs1 )
|
|
{
|
|
UINT16 result = mem_mask;
|
|
for (int i = 0; i < 2; i++)
|
|
if (m_slot[i]->dev() != NULL)
|
|
result &= m_slot[i]->dev()->read_cs1(space, offset, mem_mask);
|
|
|
|
// printf( "read cs1 %04x %04x %04x\n", offset, result, mem_mask );
|
|
|
|
return result;
|
|
}
|
|
|
|
READ8_MEMBER( ide_controller_device::read_via_config )
|
|
{
|
|
UINT16 result = 0;
|
|
|
|
/* logit */
|
|
LOG(("%s:IDE via config read at %X, mem_mask=%d\n", machine().describe_context(), offset, mem_mask));
|
|
|
|
switch(offset)
|
|
{
|
|
/* unknown config register */
|
|
case IDE_BANK2_CONFIG_UNK:
|
|
result = m_config_unknown;
|
|
break;
|
|
|
|
/* active config register */
|
|
case IDE_BANK2_CONFIG_REGISTER:
|
|
result = m_config_register_num;
|
|
break;
|
|
|
|
/* data from active config register */
|
|
case IDE_BANK2_CONFIG_DATA:
|
|
if (m_config_register_num < IDE_CONFIG_REGISTERS)
|
|
result = m_config_register[m_config_register_num];
|
|
break;
|
|
|
|
default:
|
|
logerror("%s:unknown IDE via config read at %03X, mem_mask=%d\n", machine().describe_context(), offset, mem_mask);
|
|
break;
|
|
}
|
|
|
|
// printf( "read via config %04x %04x %04x\n", offset, result, mem_mask );
|
|
return result;
|
|
}
|
|
|
|
READ16_MEMBER( ide_controller_device::read_cs0_pc )
|
|
{
|
|
if (mem_mask == 0xffff && offset == 1 ) offset = 0; // hack for 32 bit read of data register
|
|
if (mem_mask == 0xff00)
|
|
{
|
|
return read_cs0(space, (offset * 2) + 1, 0xff) << 8;
|
|
}
|
|
else
|
|
{
|
|
return read_cs0(space, offset * 2, mem_mask);
|
|
}
|
|
}
|
|
|
|
READ16_MEMBER( ide_controller_device::read_cs1_pc )
|
|
{
|
|
if (mem_mask == 0xff00)
|
|
{
|
|
return read_cs1(space, (offset * 2) + 1, 0xff) << 8;
|
|
}
|
|
else
|
|
{
|
|
return read_cs1(space, offset * 2, mem_mask);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************
|
|
*
|
|
* IDE controller write
|
|
*
|
|
*************************************/
|
|
|
|
void ide_controller_device::write_dma( UINT16 data )
|
|
{
|
|
// printf( "write_dma %04x\n", data );
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
if (m_slot[i]->dev() != NULL)
|
|
m_slot[i]->dev()->write_dma(data);
|
|
}
|
|
|
|
WRITE16_MEMBER( ide_controller_device::write_cs0 )
|
|
{
|
|
// printf( "write cs0 %04x %04x %04x\n", offset, data, mem_mask );
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
if (m_slot[i]->dev() != NULL)
|
|
m_slot[i]->dev()->write_cs0(space, offset, data, mem_mask);
|
|
}
|
|
|
|
WRITE16_MEMBER( ide_controller_device::write_cs1 )
|
|
{
|
|
// printf( "write cs1 %04x %04x %04x\n", offset, data, mem_mask );
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
if (m_slot[i]->dev() != NULL)
|
|
m_slot[i]->dev()->write_cs1(space, offset, data, mem_mask);
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( ide_controller_device::write_dmack )
|
|
{
|
|
// printf( "write_dmack %04x\n", state );
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
if (m_slot[i]->dev() != NULL)
|
|
m_slot[i]->dev()->write_dmack(state);
|
|
}
|
|
|
|
WRITE8_MEMBER( ide_controller_device::write_via_config )
|
|
{
|
|
// printf( "write via config %04x %04x %04x\n", offset, data, mem_mask );
|
|
|
|
/* logit */
|
|
LOG(("%s:IDE via config write to %X = %08X, mem_mask=%d\n", machine().describe_context(), offset, data, mem_mask));
|
|
|
|
switch (offset)
|
|
{
|
|
/* unknown config register */
|
|
case IDE_BANK2_CONFIG_UNK:
|
|
m_config_unknown = data;
|
|
break;
|
|
|
|
/* active config register */
|
|
case IDE_BANK2_CONFIG_REGISTER:
|
|
m_config_register_num = data;
|
|
break;
|
|
|
|
/* data from active config register */
|
|
case IDE_BANK2_CONFIG_DATA:
|
|
if (m_config_register_num < IDE_CONFIG_REGISTERS)
|
|
m_config_register[m_config_register_num] = data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
WRITE16_MEMBER( ide_controller_device::write_cs0_pc )
|
|
{
|
|
if (mem_mask == 0xffff && offset == 1 ) offset = 0; // hack for 32 bit write to data register
|
|
if (mem_mask == 0xff00)
|
|
{
|
|
return write_cs0(space, (offset * 2) + 1, data >> 8, 0xff);
|
|
}
|
|
else
|
|
{
|
|
return write_cs0(space, offset * 2, data, mem_mask);
|
|
}
|
|
}
|
|
|
|
WRITE16_MEMBER( ide_controller_device::write_cs1_pc )
|
|
{
|
|
if (mem_mask == 0xff00)
|
|
{
|
|
return write_cs1(space, (offset * 2) + 1, data >> 8, 0xff);
|
|
}
|
|
else
|
|
{
|
|
return write_cs1(space, offset * 2, data, mem_mask);
|
|
}
|
|
}
|
|
|
|
SLOT_INTERFACE_START(ide_devices)
|
|
SLOT_INTERFACE("hdd", IDE_HARDDISK)
|
|
SLOT_INTERFACE_END
|
|
|
|
ide_controller_device::ide_controller_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) :
|
|
device_t(mconfig, type, name, tag, owner, clock),
|
|
m_config_unknown(0),
|
|
m_config_register_num(0),
|
|
m_irq_handler(*this),
|
|
m_dmarq_handler(*this)
|
|
{
|
|
}
|
|
|
|
|
|
const device_type IDE_CONTROLLER = &device_creator<ide_controller_device>;
|
|
|
|
ide_controller_device::ide_controller_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
|
|
device_t(mconfig, IDE_CONTROLLER, "IDE Controller", tag, owner, clock),
|
|
m_config_unknown(0),
|
|
m_config_register_num(0),
|
|
m_irq_handler(*this),
|
|
m_dmarq_handler(*this)
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// device_start - device-specific startup
|
|
//-------------------------------------------------
|
|
|
|
void ide_controller_device::device_start()
|
|
{
|
|
m_irq_handler.resolve_safe();
|
|
m_dmarq_handler.resolve_safe();
|
|
|
|
/* set MAME harddisk handle */
|
|
m_slot[0] = subdevice<ide_slot_device>("0");
|
|
m_slot[1] = subdevice<ide_slot_device>("1");
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
m_irq[i] = 0;
|
|
m_dmarq[i] = 0;
|
|
|
|
ide_device_interface *dev = m_slot[i]->dev();
|
|
if (dev != NULL)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
dev->m_irq_handler.set_callback(DEVCB2_DEVWRITELINE("^", ide_controller_device, irq0_write_line));
|
|
dev->m_dmarq_handler.set_callback(DEVCB2_DEVWRITELINE("^", ide_controller_device, dmarq0_write_line));
|
|
}
|
|
else
|
|
{
|
|
dev->m_irq_handler.set_callback(DEVCB2_DEVWRITELINE("^", ide_controller_device, irq1_write_line));
|
|
dev->m_dmarq_handler.set_callback(DEVCB2_DEVWRITELINE("^", ide_controller_device, dmarq1_write_line));
|
|
}
|
|
|
|
dev->write_csel(i);
|
|
dev->write_dasp(m_slot[1]->dev() != NULL);
|
|
}
|
|
}
|
|
|
|
/* register ide states */
|
|
save_item(NAME(m_config_unknown));
|
|
save_item(NAME(m_config_register));
|
|
save_item(NAME(m_config_register_num));
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// device_reset - device-specific reset
|
|
//-------------------------------------------------
|
|
|
|
void ide_controller_device::device_reset()
|
|
{
|
|
LOG(("IDE controller reset performed\n"));
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// IDE SLOT DEVICE
|
|
//**************************************************************************
|
|
|
|
// device type definition
|
|
const device_type IDE_SLOT = &device_creator<ide_slot_device>;
|
|
|
|
//-------------------------------------------------
|
|
// ide_slot_device - constructor
|
|
//-------------------------------------------------
|
|
|
|
ide_slot_device::ide_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
|
: device_t(mconfig, IDE_SLOT, "IDE Connector", tag, owner, clock),
|
|
device_slot_interface(mconfig, *this),
|
|
m_dev(NULL)
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// device_config_complete - perform any
|
|
// operations now that the configuration is
|
|
// complete
|
|
//-------------------------------------------------
|
|
|
|
void ide_slot_device::device_config_complete()
|
|
{
|
|
m_dev = dynamic_cast<ide_device_interface *>(get_card_device());
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// device_start - device-specific startup
|
|
//-------------------------------------------------
|
|
|
|
void ide_slot_device::device_start()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
#define IDE_BUSMASTER_STATUS_ACTIVE 0x01
|
|
#define IDE_BUSMASTER_STATUS_ERROR 0x02
|
|
#define IDE_BUSMASTER_STATUS_IRQ 0x04
|
|
|
|
const device_type BUS_MASTER_IDE_CONTROLLER = &device_creator<bus_master_ide_controller_device>;
|
|
|
|
bus_master_ide_controller_device::bus_master_ide_controller_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
|
|
ide_controller_device(mconfig, BUS_MASTER_IDE_CONTROLLER, "Bus Master IDE Controller", tag, owner, clock),
|
|
dma_address(0),
|
|
dma_bytes_left(0),
|
|
dma_descriptor(0),
|
|
dma_last_buffer(0),
|
|
bus_master_command(0),
|
|
bus_master_status(0),
|
|
bus_master_descriptor(0),
|
|
m_irq(0),
|
|
m_dmarq(0)
|
|
{
|
|
}
|
|
|
|
void bus_master_ide_controller_device::device_start()
|
|
{
|
|
ide_controller_device::device_start();
|
|
|
|
/* find the bus master space */
|
|
if (bmcpu != NULL)
|
|
{
|
|
device_t *bmtarget = machine().device(bmcpu);
|
|
if (bmtarget == NULL)
|
|
throw emu_fatalerror("IDE controller '%s' bus master target '%s' doesn't exist!", tag(), bmcpu);
|
|
device_memory_interface *memory;
|
|
if (!bmtarget->interface(memory))
|
|
throw emu_fatalerror("IDE controller '%s' bus master target '%s' has no memory!", tag(), bmcpu);
|
|
dma_space = &memory->space(bmspace);
|
|
dma_address_xor = (dma_space->endianness() == ENDIANNESS_LITTLE) ? 0 : 3;
|
|
}
|
|
|
|
save_item(NAME(dma_address));
|
|
save_item(NAME(dma_bytes_left));
|
|
save_item(NAME(dma_descriptor));
|
|
save_item(NAME(dma_last_buffer));
|
|
save_item(NAME(bus_master_command));
|
|
save_item(NAME(bus_master_status));
|
|
save_item(NAME(bus_master_descriptor));
|
|
}
|
|
|
|
void bus_master_ide_controller_device::set_irq(int state)
|
|
{
|
|
ide_controller_device::set_irq(state);
|
|
|
|
if (m_irq != state)
|
|
{
|
|
m_irq = state;
|
|
|
|
if( m_irq )
|
|
bus_master_status |= IDE_BUSMASTER_STATUS_IRQ;
|
|
}
|
|
}
|
|
|
|
void bus_master_ide_controller_device::set_dmarq(int state)
|
|
{
|
|
ide_controller_device::set_dmarq(state);
|
|
|
|
if (m_dmarq != state)
|
|
{
|
|
m_dmarq = state;
|
|
|
|
execute_dma();
|
|
}
|
|
}
|
|
|
|
/*************************************
|
|
*
|
|
* Bus master read
|
|
*
|
|
*************************************/
|
|
|
|
READ32_MEMBER( bus_master_ide_controller_device::ide_bus_master32_r )
|
|
{
|
|
LOG(("%s:ide_bus_master32_r(%d, %08x)\n", machine().describe_context(), offset, mem_mask));
|
|
|
|
switch( offset )
|
|
{
|
|
case 0:
|
|
/* command register/status register */
|
|
return bus_master_command | (bus_master_status << 16);
|
|
|
|
case 1:
|
|
/* descriptor table register */
|
|
return bus_master_descriptor;
|
|
}
|
|
|
|
return 0xffffffff;
|
|
}
|
|
|
|
|
|
|
|
/*************************************
|
|
*
|
|
* Bus master write
|
|
*
|
|
*************************************/
|
|
|
|
WRITE32_MEMBER( bus_master_ide_controller_device::ide_bus_master32_w )
|
|
{
|
|
LOG(("%s:ide_bus_master32_w(%d, %08x, %08X)\n", machine().describe_context(), offset, mem_mask, data));
|
|
|
|
switch( offset )
|
|
{
|
|
case 0:
|
|
if( ACCESSING_BITS_0_7 )
|
|
{
|
|
/* command register */
|
|
UINT8 old = bus_master_command;
|
|
UINT8 val = data & 0xff;
|
|
|
|
/* save the read/write bit and the start/stop bit */
|
|
bus_master_command = (old & 0xf6) | (val & 0x09);
|
|
|
|
if ((old ^ bus_master_command) & 1)
|
|
{
|
|
if (bus_master_command & 1)
|
|
{
|
|
/* handle starting a transfer */
|
|
bus_master_status |= IDE_BUSMASTER_STATUS_ACTIVE;
|
|
|
|
/* reset all the DMA data */
|
|
dma_bytes_left = 0;
|
|
dma_descriptor = bus_master_descriptor;
|
|
|
|
/* if we're going live, start the pending read/write */
|
|
execute_dma();
|
|
}
|
|
else if (bus_master_status & IDE_BUSMASTER_STATUS_ACTIVE)
|
|
{
|
|
bus_master_status &= ~IDE_BUSMASTER_STATUS_ACTIVE;
|
|
|
|
LOG(("DMA Aborted!\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
if( ACCESSING_BITS_16_23 )
|
|
{
|
|
/* status register */
|
|
UINT8 old = bus_master_status;
|
|
UINT8 val = data >> 16;
|
|
|
|
/* save the DMA capable bits */
|
|
bus_master_status = (old & 0x9f) | (val & 0x60);
|
|
|
|
/* clear interrupt and error bits */
|
|
if (val & IDE_BUSMASTER_STATUS_IRQ)
|
|
bus_master_status &= ~IDE_BUSMASTER_STATUS_IRQ;
|
|
if (val & IDE_BUSMASTER_STATUS_ERROR)
|
|
bus_master_status &= ~IDE_BUSMASTER_STATUS_ERROR;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
/* descriptor table register */
|
|
bus_master_descriptor = data & 0xfffffffc;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void bus_master_ide_controller_device::execute_dma()
|
|
{
|
|
write_dmack(ASSERT_LINE);
|
|
|
|
while (m_dmarq && (bus_master_status & IDE_BUSMASTER_STATUS_ACTIVE))
|
|
{
|
|
/* if we're out of space, grab the next descriptor */
|
|
if (dma_bytes_left == 0)
|
|
{
|
|
/* fetch the address */
|
|
dma_address = dma_space->read_byte(dma_descriptor++ ^ dma_address_xor);
|
|
dma_address |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 8;
|
|
dma_address |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 16;
|
|
dma_address |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 24;
|
|
dma_address &= 0xfffffffe;
|
|
|
|
/* fetch the length */
|
|
dma_bytes_left = dma_space->read_byte(dma_descriptor++ ^ dma_address_xor);
|
|
dma_bytes_left |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 8;
|
|
dma_bytes_left |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 16;
|
|
dma_bytes_left |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 24;
|
|
dma_last_buffer = (dma_bytes_left >> 31) & 1;
|
|
dma_bytes_left &= 0xfffe;
|
|
if (dma_bytes_left == 0)
|
|
dma_bytes_left = 0x10000;
|
|
|
|
// LOG(("New DMA descriptor: address = %08X bytes = %04X last = %d\n", dma_address, dma_bytes_left, dma_last_buffer));
|
|
}
|
|
|
|
if (bus_master_command & 8)
|
|
{
|
|
// read from ata bus
|
|
UINT16 data = read_dma();
|
|
|
|
// write to memory
|
|
dma_space->write_byte(dma_address++, data & 0xff);
|
|
dma_space->write_byte(dma_address++, data >> 8);
|
|
}
|
|
else
|
|
{
|
|
// read from memory;
|
|
UINT16 data = dma_space->read_byte(dma_address++);
|
|
data |= dma_space->read_byte(dma_address++) << 8;
|
|
|
|
// write to ata bus
|
|
write_dma(data);
|
|
}
|
|
|
|
dma_bytes_left -= 2;
|
|
|
|
if (dma_bytes_left == 0 && dma_last_buffer)
|
|
{
|
|
bus_master_status &= ~IDE_BUSMASTER_STATUS_ACTIVE;
|
|
|
|
if (m_dmarq)
|
|
{
|
|
LOG(("DMA Out of buffer space!\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
write_dmack(CLEAR_LINE);
|
|
}
|