summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/am79c90.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/am79c90.cpp')
-rw-r--r--src/devices/machine/am79c90.cpp98
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);