// license:BSD-3-Clause // copyright-holders:smf /*************************************************************************** idectrl.c Generic (PC-style) IDE controller implementation. ***************************************************************************/ #include "emu.h" #include "idectrl.h" /*************************************************************************** DEBUGGING ***************************************************************************/ #define VERBOSE 0 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) /*************************************************************************** CONSTANTS ***************************************************************************/ const device_type IDE_CONTROLLER = device_creator; ide_controller_device::ide_controller_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ata_interface_device(mconfig, IDE_CONTROLLER, "IDE Controller", tag, owner, clock, "ide_controller", __FILE__) { } ide_controller_device::ide_controller_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, uint32_t clock, const char *shortname, const char *source) : ata_interface_device(mconfig, type, name, tag, owner, clock, shortname, source) { } READ16_MEMBER( ide_controller_device::read_cs0 ) { if (mem_mask == 0xffff && offset == 1 ){ offset = 0; popmessage( "requires ide_controller_32_device" ); } if (mem_mask == 0xff00) { return ata_interface_device::read_cs0(space, (offset * 2) + 1, 0xff) << 8; } else { return ata_interface_device::read_cs0(space, offset * 2, mem_mask); } } READ16_MEMBER( ide_controller_device::read_cs1 ) { if (mem_mask == 0xff00) { return ata_interface_device::read_cs1(space, (offset * 2) + 1, 0xff) << 8; } else { return ata_interface_device::read_cs1(space, offset * 2, mem_mask); } } WRITE16_MEMBER( ide_controller_device::write_cs0 ) { if (mem_mask == 0xffff && offset == 1 ){ offset = 0; popmessage( "requires ide_controller_32_device" ); } if (mem_mask == 0xff00) { return ata_interface_device::write_cs0(space, (offset * 2) + 1, data >> 8, 0xff); } else { return ata_interface_device::write_cs0(space, offset * 2, data, mem_mask); } } WRITE16_MEMBER( ide_controller_device::write_cs1 ) { if (mem_mask == 0xff00) { return ata_interface_device::write_cs1(space, (offset * 2) + 1, data >> 8, 0xff); } else { return ata_interface_device::write_cs1(space, offset * 2, data, mem_mask); } } const device_type IDE_CONTROLLER_32 = device_creator; ide_controller_32_device::ide_controller_32_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ide_controller_device(mconfig, IDE_CONTROLLER_32, "IDE Controller (32 bit)", tag, owner, clock, "ide_controller32", __FILE__) { } ide_controller_32_device::ide_controller_32_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, uint32_t clock, const char *shortname, const char *source) : ide_controller_device(mconfig, type, name, tag, owner, clock, shortname, source) { } READ32_MEMBER(ide_controller_32_device::read_cs0) { uint32_t data = 0; if (ACCESSING_BITS_0_15) { data = ide_controller_device::read_cs0(space, (offset * 2), mem_mask); if (offset == 0 && ACCESSING_BITS_16_31) data |= ide_controller_device::read_cs0(space, (offset * 2), mem_mask >> 16) << 16; } else if (ACCESSING_BITS_16_31) { data = ide_controller_device::read_cs0(space, (offset * 2) + 1, mem_mask >> 16) << 16; } return data; } READ32_MEMBER(ide_controller_32_device::read_cs1) { uint32_t data = 0; if (ACCESSING_BITS_0_15) { data = ide_controller_device::read_cs1(space, (offset * 2), mem_mask); } else if (ACCESSING_BITS_16_23) { data = ide_controller_device::read_cs1(space, (offset * 2) + 1, mem_mask >> 16) << 16; } return data; } WRITE32_MEMBER(ide_controller_32_device::write_cs0) { if (ACCESSING_BITS_0_15) { ide_controller_device::write_cs0(space, (offset * 2), data, mem_mask); if (offset == 0 && ACCESSING_BITS_16_31) ata_interface_device::write_cs0(space, (offset * 2), data >> 16, mem_mask >> 16); } else if (ACCESSING_BITS_16_31) { ide_controller_device::write_cs0(space, (offset * 2) + 1, data >> 16, mem_mask >> 16); } } WRITE32_MEMBER(ide_controller_32_device::write_cs1) { if (ACCESSING_BITS_0_15) { ide_controller_device::write_cs1(space, (offset * 2), data, mem_mask); } else if (ACCESSING_BITS_16_31) { ide_controller_device::write_cs1(space, (offset * 2) + 1, data >> 16, mem_mask >> 16); } } #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(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ide_controller_32_device(mconfig, BUS_MASTER_IDE_CONTROLLER, "Bus Master IDE Controller", tag, owner, clock, "bus_master_ide_controller", __FILE__), m_dma_address(0), m_dma_bytes_left(0), m_dma_descriptor(0), m_dma_last_buffer(0), m_bus_master_command(0), m_bus_master_status(0), m_bus_master_descriptor(0), m_irq(0), m_dmarq(0) { } void bus_master_ide_controller_device::device_start() { ide_controller_32_device::device_start(); /* find the bus master space */ if (m_bmcpu != nullptr) { device_t *bmtarget = machine().device(m_bmcpu); if (bmtarget == nullptr) throw emu_fatalerror("IDE controller '%s' bus master target '%s' doesn't exist!", tag(), m_bmcpu); device_memory_interface *memory; if (!bmtarget->interface(memory)) throw emu_fatalerror("IDE controller '%s' bus master target '%s' has no memory!", tag(), m_bmcpu); m_dma_space = &memory->space(m_bmspace); m_dma_address_xor = (m_dma_space->endianness() == ENDIANNESS_LITTLE) ? 0 : 3; } save_item(NAME(m_dma_address)); save_item(NAME(m_dma_bytes_left)); save_item(NAME(m_dma_descriptor)); save_item(NAME(m_dma_last_buffer)); save_item(NAME(m_bus_master_command)); save_item(NAME(m_bus_master_status)); save_item(NAME(m_bus_master_descriptor)); } void bus_master_ide_controller_device::set_irq(int state) { ata_interface_device::set_irq(state); if (m_irq != state) { m_irq = state; if( m_irq ) m_bus_master_status |= IDE_BUSMASTER_STATUS_IRQ; } } void bus_master_ide_controller_device::set_dmarq(int state) { ata_interface_device::set_dmarq(state); if (m_dmarq != state) { m_dmarq = state; execute_dma(); } } /************************************* * * Bus master read * *************************************/ READ32_MEMBER( bus_master_ide_controller_device::bmdma_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 m_bus_master_command | (m_bus_master_status << 16); case 1: /* descriptor table register */ return m_bus_master_descriptor; } return 0xffffffff; } /************************************* * * Bus master write * *************************************/ WRITE32_MEMBER( bus_master_ide_controller_device::bmdma_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 ) { /* Bus Master IDE Command register */ uint8_t old = m_bus_master_command; uint8_t val = data & 0xff; /* save the "Read or Write Control" bit 3 and the "Start/Stop Bus Master" bit 0 */ m_bus_master_command = (old & 0xf6) | (val & 0x09); if ((old ^ m_bus_master_command) & 1) { if (m_bus_master_command & 1) { /* handle starting a transfer */ m_bus_master_status |= IDE_BUSMASTER_STATUS_ACTIVE; /* reset all the DMA data */ m_dma_bytes_left = 0; m_dma_descriptor = m_bus_master_descriptor; /* if we're going live, start the pending read/write */ execute_dma(); } else if (m_bus_master_status & IDE_BUSMASTER_STATUS_ACTIVE) { m_bus_master_status &= ~IDE_BUSMASTER_STATUS_ACTIVE; LOG(("DMA Aborted!\n")); } } } if( ACCESSING_BITS_16_23 ) { /* Bus Master IDE Status register */ uint8_t old = m_bus_master_status; uint8_t val = data >> 16; /* save the DMA capable bits */ m_bus_master_status = (old & 0x9f) | (val & 0x60); /* clear interrupt and error bits */ if (val & IDE_BUSMASTER_STATUS_IRQ) m_bus_master_status &= ~IDE_BUSMASTER_STATUS_IRQ; if (val & IDE_BUSMASTER_STATUS_ERROR) m_bus_master_status &= ~IDE_BUSMASTER_STATUS_ERROR; } break; case 1: /* descriptor table register */ m_bus_master_descriptor = data & 0xfffffffc; break; } } void bus_master_ide_controller_device::execute_dma() { write_dmack(ASSERT_LINE); while (m_dmarq && (m_bus_master_status & IDE_BUSMASTER_STATUS_ACTIVE)) { /* if we're out of space, grab the next Physical Region Descriptor */ if (m_dma_bytes_left == 0) { /* fetch the address */ m_dma_address = m_dma_space->read_byte(m_dma_descriptor++ ^ m_dma_address_xor); m_dma_address |= m_dma_space->read_byte(m_dma_descriptor++ ^ m_dma_address_xor) << 8; m_dma_address |= m_dma_space->read_byte(m_dma_descriptor++ ^ m_dma_address_xor) << 16; m_dma_address |= m_dma_space->read_byte(m_dma_descriptor++ ^ m_dma_address_xor) << 24; m_dma_address &= 0xfffffffe; /* fetch the length */ m_dma_bytes_left = m_dma_space->read_byte(m_dma_descriptor++ ^ m_dma_address_xor); m_dma_bytes_left |= m_dma_space->read_byte(m_dma_descriptor++ ^ m_dma_address_xor) << 8; m_dma_bytes_left |= m_dma_space->read_byte(m_dma_descriptor++ ^ m_dma_address_xor) << 16; m_dma_bytes_left |= m_dma_space->read_byte(m_dma_descriptor++ ^ m_dma_address_xor) << 24; m_dma_last_buffer = (m_dma_bytes_left >> 31) & 1; m_dma_bytes_left &= 0xfffe; if (m_dma_bytes_left == 0) m_dma_bytes_left = 0x10000; // LOG(("New DMA descriptor: address = %08X bytes = %04X last = %d\n", m_dma_address, m_dma_bytes_left, m_dma_last_buffer)); } if (m_bus_master_command & 8) { // read from ata bus uint16_t data = read_dma(); // write to memory m_dma_space->write_byte(m_dma_address++, data & 0xff); m_dma_space->write_byte(m_dma_address++, data >> 8); } else { // read from memory; uint16_t data = m_dma_space->read_byte(m_dma_address++); data |= m_dma_space->read_byte(m_dma_address++) << 8; // write to ata bus write_dma(data); } m_dma_bytes_left -= 2; if (m_dma_bytes_left == 0 && m_dma_last_buffer) { m_bus_master_status &= ~IDE_BUSMASTER_STATUS_ACTIVE; if (m_dmarq) { LOG(("DMA Out of buffer space!\n")); } } } write_dmack(CLEAR_LINE); }