diff options
Diffstat (limited to 'src/devices/machine/am79c90.cpp')
-rw-r--r-- | src/devices/machine/am79c90.cpp | 98 |
1 files changed, 51 insertions, 47 deletions
diff --git a/src/devices/machine/am79c90.cpp b/src/devices/machine/am79c90.cpp index 5ca3515ffc1..c2e56692461 100644 --- a/src/devices/machine/am79c90.cpp +++ b/src/devices/machine/am79c90.cpp @@ -6,11 +6,10 @@ * CMOS Local Area Network Controller for Ethernet (C-LANCE). * * Sources: + * - Am7990 Local Area Network Controller for Ethernet (LANCE), Publication #05698, Rev. C, June 1990, Advanced Micro Devices + * - Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE), Publication #17881, Rev. C, January 1998, Advanced Micro Devices * - * http://bitsavers.org/components/amd/Am7990/Am7990.pdf - * http://bitsavers.org/components/amd/Am7990/Am79c90.pdf - * - * TODO + * TODO: * - external loopback * - hp9k/3xx diagnostic failures * @@ -44,7 +43,8 @@ #include "emu.h" #include "am79c90.h" -#define LOG_GENERAL (1U << 0) +#include "multibyte.h" + #define LOG_REG (1U << 1) #define LOG_INIT (1U << 2) #define LOG_RXTX (1U << 3) @@ -59,10 +59,11 @@ DEFINE_DEVICE_TYPE(AM79C90, am79c90_device, "am79c90", "Am79C90 C-LANCE Ethernet am7990_device_base::am7990_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, type, tag, owner, clock) - , device_network_interface(mconfig, *this, 10.0f) + , device_network_interface(mconfig, *this, 10) , m_intr_out_cb(*this) - , m_dma_in_cb(*this) + , m_dma_in_cb(*this, 0) , m_dma_out_cb(*this) + , m_interrupt(nullptr) , m_transmit_poll(nullptr) , m_intr_out_state(1) { @@ -83,11 +84,8 @@ constexpr attotime am7990_device_base::TX_POLL_PERIOD; void am7990_device_base::device_start() { - m_intr_out_cb.resolve_safe(); - m_dma_in_cb.resolve_safe(0); - m_dma_out_cb.resolve_safe(); - - m_transmit_poll = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(am7990_device_base::transmit_poll), this)); + m_interrupt = timer_alloc(FUNC(am7990_device_base::interrupt), this); + m_transmit_poll = timer_alloc(FUNC(am7990_device_base::transmit_poll), this); m_transmit_poll->adjust(TX_POLL_PERIOD, 0, TX_POLL_PERIOD); save_item(NAME(m_rap)); @@ -126,16 +124,23 @@ void am7990_device_base::device_reset() update_interrupts(); } -void am7990_device_base::update_interrupts() +void am7990_device_base::interrupt(s32 param) +{ + m_intr_out_cb(param); +} + +void am7990_device_base::update_interrupts(attotime const delay) { - if (m_csr[0] & CSR0_INTR) + if (m_csr[0] & CSR0_INEA) { - // assert intr if interrupts are enabled and not asserted - if ((m_csr[0] & CSR0_INEA) && m_intr_out_state) + // update intr + if (bool(m_csr[0] & CSR0_INTR) == m_intr_out_state) { m_intr_out_state = !m_intr_out_state; - m_intr_out_cb(m_intr_out_state); - LOG("interrupt asserted\n"); + m_interrupt->adjust(delay, m_intr_out_state); + + if (!m_intr_out_state) + LOG("interrupt asserted\n"); } } else @@ -144,7 +149,7 @@ void am7990_device_base::update_interrupts() if (!m_intr_out_state) { m_intr_out_state = !m_intr_out_state; - m_intr_out_cb(m_intr_out_state); + m_interrupt->adjust(delay, m_intr_out_state); } } } @@ -289,7 +294,7 @@ void am7990_device_base::recv_complete_cb(int result) LOGMASKED(LOG_RXTX, "receive complete rmd1 0x%04x rmd3 %d\n", m_rx_md[1], result & RMD3_MCNT); m_dma_out_cb(ring_address | 2, m_rx_md[1]); - m_dma_out_cb(ring_address | 6, result & RMD3_MCNT); + m_dma_out_cb(ring_address | 6, (m_rx_md[1] & RMD1_ERR) ? 0 : (result & RMD3_MCNT)); // advance the ring m_rx_ring_pos = (m_rx_ring_pos + 1) & m_rx_ring_mask; @@ -302,7 +307,7 @@ void am7990_device_base::recv_complete_cb(int result) update_interrupts(); } -void am7990_device_base::transmit_poll(void *ptr, s32 param) +void am7990_device_base::transmit_poll(s32 param) { // check transmitter enabled if (m_csr[0] & CSR0_TXON) @@ -442,10 +447,8 @@ void am7990_device_base::transmit() u32 const crc = util::crc32_creator::simple(buf, length); // insert the fcs - buf[length++] = crc >> 0; - buf[length++] = crc >> 8; - buf[length++] = crc >> 16; - buf[length++] = crc >> 24; + put_u32le(&buf[length], crc); + length += 4; } LOGMASKED(LOG_RXTX, "transmit sending packet length %d\n", length); @@ -482,7 +485,7 @@ void am7990_device_base::transmit() } } - send(buf, length); + send(buf, length, 4); } void am7990_device_base::send_complete_cb(int result) @@ -525,7 +528,7 @@ void am7990_device_base::send_complete_cb(int result) m_transmit_poll->adjust(attotime::zero, 0, TX_POLL_PERIOD); } -READ16_MEMBER(am7990_device_base::regs_r) +u16 am7990_device_base::regs_r(address_space &space, offs_t offset) { if (!offset) { @@ -540,12 +543,14 @@ READ16_MEMBER(am7990_device_base::regs_r) return m_rap; } -WRITE16_MEMBER(am7990_device_base::regs_w) +void am7990_device_base::regs_w(offs_t offset, u16 data) { if (!offset) { LOGMASKED(LOG_REG, "regs_w csr%d data 0x%04x (%s)\n", m_rap, data, machine().describe_context()); + attotime delay = attotime::zero; + switch (m_rap) { case 0: // Control/Status @@ -574,7 +579,17 @@ WRITE16_MEMBER(am7990_device_base::regs_w) if ((data & CSR0_INIT) && !(m_csr[0] & CSR0_INIT)) { if (m_csr[0] & CSR0_STOP) + { initialize(); + + /* + * Initialization reads 12 words from the bus using single + * word DMA transfers. Allow 2 cycles for bus acquisition + * and release, plus 6 cycles for each single word DMA + * transfer. + */ + delay = attotime::from_ticks((1 + 6 + 1) * 12, clock()); + } else m_csr[0] |= m_idon ? CSR0_IDON : CSR0_INIT; } @@ -652,7 +667,7 @@ WRITE16_MEMBER(am7990_device_base::regs_w) else m_csr[0] &= ~CSR0_INTR; - update_interrupts(); + update_interrupts(delay); break; case 1: // Least significant 15 bits of the Initialization Block @@ -694,15 +709,10 @@ void am7990_device_base::initialize() m_mode = init_block[0]; - set_promisc(m_mode & MODE_PROM); - - m_physical_addr[0] = init_block[1]; - m_physical_addr[1] = init_block[1] >> 8; - m_physical_addr[2] = init_block[2]; - m_physical_addr[3] = init_block[2] >> 8; - m_physical_addr[4] = init_block[3]; - m_physical_addr[5] = init_block[3] >> 8; - set_mac((char *)m_physical_addr); + put_u16le(&m_physical_addr[0], init_block[1]); + put_u16le(&m_physical_addr[2], init_block[2]); + put_u16le(&m_physical_addr[4], init_block[3]); + set_mac(m_physical_addr); m_logical_addr_filter = (u64(init_block[7]) << 48) | (u64(init_block[6]) << 32) | (u32(init_block[5]) << 16) | init_block[4]; @@ -749,15 +759,9 @@ void am7990_device_base::dma_in(u32 address, u8 *buf, int length) u16 const word = m_dma_in_cb(address); if (m_csr[3] & CSR3_BSWP) - { - buf[0] = word >> 8; - buf[1] = word & 0xff; - } + put_u16be(&buf[0], word); else - { - buf[0] = word & 0xff; - buf[1] = word >> 8; - } + put_u16le(&buf[0], word); buf += 2; address += 2; @@ -798,7 +802,7 @@ void am7990_device_base::dma_out(u32 address, u8 *buf, int length) // word loop while (length > 1) { - u16 const word = (m_csr[3] & CSR3_BSWP) ? (buf[0] << 8) | buf[1] : (buf[1] << 8) | buf[0]; + u16 const word = (m_csr[3] & CSR3_BSWP) ? get_u16be(&buf[0]) : get_u16le(&buf[0]); m_dma_out_cb(address, word); |