From a24d9ab6aef66a0b11708be73db1b1c1ee84cf76 Mon Sep 17 00:00:00 2001 From: mooglyguy Date: Sat, 8 Sep 2018 18:03:04 +0200 Subject: [PATCH] -am79c90: Added rudimentary AMD 79C90 LANCE ethernet controller support, enough to make sun4 happy. [Ryan Holtz] --- scripts/src/machine.lua | 12 + scripts/target/mame/mess.lua | 1 + src/devices/machine/am79c90.cpp | 570 ++++++++++++++++++++++++++++++++ src/devices/machine/am79c90.h | 202 +++++++++++ src/mame/drivers/sun4.cpp | 159 +++------ 5 files changed, 832 insertions(+), 112 deletions(-) create mode 100644 src/devices/machine/am79c90.cpp create mode 100644 src/devices/machine/am79c90.h diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index af0d98782a9..7c1cc8e66ca 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -610,6 +610,18 @@ if (MACHINES["AM53CF96"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/am79c90.h,MACHINES["AM79C90"] = true +--------------------------------------------------- + +if (MACHINES["AM79C90"]~=null) then + files { + MAME_DIR .. "src/devices/machine/am79c90.cpp", + MAME_DIR .. "src/devices/machine/am79c90.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/am9513.h,MACHINES["AM9513"] = true diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 2680ba1fdfa..45d3efafb20 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -396,6 +396,7 @@ MACHINES["ADC1213X"] = true MACHINES["AICARTC"] = true MACHINES["AM2847"] = true MACHINES["AM53CF96"] = true +MACHINES["AM79C90"] = true MACHINES["AM9513"] = true MACHINES["AM9517A"] = true MACHINES["AM9519"] = true diff --git a/src/devices/machine/am79c90.cpp b/src/devices/machine/am79c90.cpp new file mode 100644 index 00000000000..e8328a59e07 --- /dev/null +++ b/src/devices/machine/am79c90.cpp @@ -0,0 +1,570 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/***************************************************************************** + + AMD Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE) + + TODO: + - Communication with the outside world + - Error handling + - Clocks + +*****************************************************************************/ + +#include "emu.h" +#include "am79c90.h" + +DEFINE_DEVICE_TYPE(AM79C90, am79c90_device, "am79c90", "Am79C90 LANCE Ethernet Controller") + +am79c90_device::am79c90_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, AM79C90, tag, owner, clock) + , m_receive_timer(nullptr) + //, m_receive_poll_timer(nullptr) + , m_transmit_timer(nullptr) + , m_transmit_poll_timer(nullptr) + , m_irq_out_cb(*this) + , m_dma_out_cb(*this) + , m_dma_in_cb(*this) +{ +} + +void am79c90_device::device_start() +{ + m_irq_out_cb.resolve_safe(); + m_dma_out_cb.resolve_safe(); // TODO: Should be read/write16! + m_dma_in_cb.resolve_safe(0); + + m_transmit_poll_timer = timer_alloc(TIMER_TRANSMIT_POLL); + m_transmit_poll_timer->adjust(attotime::never); + m_transmit_timer = timer_alloc(TIMER_TRANSMIT); + m_transmit_timer->adjust(attotime::never); + m_receive_timer = timer_alloc(TIMER_RECEIVE); + m_receive_timer->adjust(attotime::never); + + save_item(NAME(m_curr_transmit_desc.m_tmd01)); + save_item(NAME(m_curr_transmit_desc.m_tmd23)); + save_item(NAME(m_next_transmit_desc.m_tmd01)); + save_item(NAME(m_next_transmit_desc.m_tmd23)); + save_item(NAME(m_curr_recv_desc.m_tmd01)); + save_item(NAME(m_curr_recv_desc.m_tmd23)); + save_item(NAME(m_next_recv_desc.m_tmd01)); + save_item(NAME(m_next_recv_desc.m_tmd23)); + save_item(NAME(m_rap)); + save_item(NAME(m_csr)); + save_item(NAME(m_mode)); + save_item(NAME(m_logical_addr_filter)); + save_item(NAME(m_physical_addr)); + + save_item(NAME(m_recv_message_count)); + save_item(NAME(m_recv_ring_addr)); + save_item(NAME(m_recv_buf_addr)); + save_item(NAME(m_recv_buf_count)); + save_item(NAME(m_recv_ring_length)); + save_item(NAME(m_recv_ring_pos)); + save_item(NAME(m_recv_fifo)); + save_item(NAME(m_recv_fifo_write)); + save_item(NAME(m_recv_fifo_read)); + save_item(NAME(m_receiving)); + + save_item(NAME(m_transmit_ring_addr)); + save_item(NAME(m_transmit_buf_addr)); + save_item(NAME(m_transmit_buf_count)); + save_item(NAME(m_transmit_ring_length)); + save_item(NAME(m_transmit_ring_pos)); + save_item(NAME(m_transmit_fifo)); + save_item(NAME(m_transmit_fifo_write)); + save_item(NAME(m_transmit_fifo_read)); + save_item(NAME(m_transmitting)); +} + +void am79c90_device::device_reset() +{ + memset(&m_curr_transmit_desc, 0, sizeof(ring_descriptor)); + memset(&m_next_transmit_desc, 0, sizeof(ring_descriptor)); + memset(&m_curr_recv_desc, 0, sizeof(ring_descriptor)); + memset(&m_next_recv_desc, 0, sizeof(ring_descriptor)); + m_rap = 0; + memset(m_csr, 0, sizeof(uint16_t) * 4); + m_csr[0] = CSR0_STOP; + m_mode = 0; + m_logical_addr_filter = 0; + m_physical_addr = 0; + + m_recv_message_count = 0; + m_recv_ring_addr = 0; + m_recv_buf_addr = 0; + m_recv_buf_count = 0; + m_recv_ring_length = 0; + m_recv_ring_pos = 0; + memset(m_recv_fifo, 0, sizeof(uint32_t) * ARRAY_LENGTH(m_recv_fifo)); + m_recv_fifo_write = 0; + m_recv_fifo_read = 0; + m_receiving = false; + + m_transmit_ring_addr = 0; + m_transmit_buf_addr = 0; + m_transmit_buf_count = 0; + m_transmit_ring_length = 0; + m_transmit_ring_pos = 0; + memset(m_transmit_fifo, 0, sizeof(uint32_t) * ARRAY_LENGTH(m_transmit_fifo)); + m_transmit_fifo_write = 0; + m_transmit_fifo_read = 0; + m_transmitting = false; +} + +void am79c90_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_TRANSMIT_POLL: + poll_transmit(); + break; + + case TIMER_TRANSMIT: + transmit(); + break; + + case TIMER_RECEIVE: + receive(); + break; + } +} + +void am79c90_device::fetch_transmit_descriptor() +{ + const uint32_t next_addr = (m_transmit_ring_addr >> 2) + (m_transmit_ring_pos << 1); + ring_descriptor &next = m_next_transmit_desc; + next.m_tmd01 = m_dma_in_cb(next_addr, ~0); + next.m_tmd23 = m_dma_in_cb(next_addr + 1, ~0); +} + +void am79c90_device::fetch_receive_descriptor() +{ + const uint32_t next_addr = (m_recv_ring_addr >> 2) + (m_recv_ring_pos << 1); + ring_descriptor &next = m_next_recv_desc; + next.m_rmd01 = m_dma_in_cb(next_addr, ~0); + next.m_rmd23 = m_dma_in_cb(next_addr + 1, ~0); +} + +void am79c90_device::recv_fifo_push(uint32_t value) +{ + // TODO: Poll for the FIFO at 1.6ms, don't instantly start receiving! + // "...If the C-LANCE does not own it, it will poll the ring once every 1.6ms until + // it owns it." + + logerror("%s: LANCE pushing %08x onto receive FIFO\n", machine().describe_context(), value); + if (!m_receiving && !(m_mode & MODE_LOOP)) + { + begin_receiving(); + } + + if (m_recv_fifo_write >= ARRAY_LENGTH(m_recv_fifo)) + { + logerror("%s: LANCE can't push onto receive FIFO, %d >= %d\n", machine().describe_context(), value, m_recv_fifo_write, ARRAY_LENGTH(m_recv_fifo)); + // TODO: Do something + return; + } + m_recv_fifo[m_recv_fifo_write] = value; + m_recv_fifo_write++; + if (m_recv_fifo_write == ARRAY_LENGTH(m_recv_fifo)) + { + // TODO: Do something + } +} + +void am79c90_device::begin_receiving() +{ + fetch_receive_descriptor(); + m_curr_recv_desc = m_next_recv_desc; + + ring_descriptor &curr = m_curr_recv_desc; + if (curr.m_rmd01 & RMD1_OWN) + { + logerror("%s: LANCE owns the current buffer, activating receive timer, RMD0123 is %08x %08x\n", machine().describe_context(), curr.m_rmd01, curr.m_rmd23); + + m_receiving = true; + m_recv_buf_addr = (((curr.m_rmd01 << 16) | (curr.m_rmd01 >> 16)) & 0x00ffffff) | 0xff000000; + const int32_t rmd2 = (int16_t)(curr.m_rmd23 >> 16); + m_recv_buf_count = (uint16_t)((-rmd2) & 0x00000fff); + if (m_recv_buf_count == 0) + m_recv_buf_count = 0x1000; + + if (m_mode & MODE_LOOP) + { + m_recv_buf_count = (m_mode & MODE_DTCR) ? 32 : 36; + } + + if (curr.m_rmd01 & RMD1_STP) + { + m_recv_message_count = 0; + } + m_receive_timer->adjust(attotime::from_hz(10'000'000), 0, attotime::from_hz(10'000'000)); + } + else + { + logerror("%s: LANCE does not own the current buffer, deactivating receive timer\n", machine().describe_context()); + m_receiving = false; + m_receive_timer->adjust(attotime::never); + } +} + +void am79c90_device::receive() +{ + const uint32_t received_value = (m_recv_fifo_write == 0) ? 0 : m_recv_fifo[m_recv_fifo_read]; + m_recv_fifo_read++; + if (m_recv_fifo_read >= m_recv_fifo_write) + { + m_recv_fifo_read = 0; + m_recv_fifo_write = 0; + } + + if (m_recv_buf_count >= 4) + { + logerror("%s: LANCE receiving %08x to address %08x, remaining %d\n", machine().describe_context(), received_value, m_recv_buf_addr, m_recv_buf_count - 4); + m_dma_out_cb(m_recv_buf_addr >> 2, received_value, ~0); + + m_recv_buf_addr += 4; + m_recv_message_count += 4; + m_recv_buf_count -= 4; + } + else + { + const uint32_t mask = 0xffffffff << (m_recv_buf_count << 3); + logerror("%s: LANCE receiving %08x & %08x to address %08x, remaining %d\n", machine().describe_context(), received_value, mask, m_recv_buf_addr, 0); + m_dma_out_cb(m_recv_buf_addr >> 2, received_value, mask); + + m_recv_buf_addr += m_recv_buf_count; + m_recv_message_count += m_recv_buf_count; + m_recv_buf_count = 0; + } + + if (m_recv_buf_count == 0) + { + logerror("%s: LANCE has completed receiving a buffer, clearing OWN bit and advancing receive ring position\n", machine().describe_context()); + ring_descriptor &curr = m_curr_recv_desc; + curr.m_rmd01 &= ~RMD1_OWN; + const uint32_t addr = (m_recv_ring_addr >> 2) + (m_recv_ring_pos << 1); + logerror("%s: LANCE is writing new RMD01: %08x\n", machine().describe_context(), curr.m_rmd01); + m_dma_out_cb(addr, curr.m_rmd01, ~0); + + if (curr.m_rmd01 & TMD1_ENP) + { + curr.m_rmd23 &= 0xfffff000; + if (m_recv_message_count != 0x1000) + { + curr.m_rmd23 |= m_recv_message_count & 0xfff; + } + + m_dma_out_cb(addr + 1, curr.m_rmd23, ~0); + + logerror("%s: LANCE has completed receiving a message, total message length %d bytes, new RMD23 %08x\n", machine().describe_context(), m_recv_message_count, curr.m_rmd23); + } + m_recv_ring_pos++; + m_recv_ring_pos &= m_recv_ring_length - 1; + + if (!(m_mode & MODE_LOOP)) + { + begin_receiving(); + } + else + { + logerror("%s: LANCE loopback test receive finished, setting RINT and stopping timer.\n", machine().describe_context()); + m_csr[0] |= CSR0_RINT; + update_interrupts(); + m_receiving = false; + m_receive_timer->adjust(attotime::never); + } + } +} + +void am79c90_device::transmit() +{ + logerror("%s: LANCE transmit, fetching from %08x\n", machine().describe_context(), m_transmit_buf_addr >> 2); + uint32_t transmit_value = 0; + const bool dtcr = m_mode & MODE_DTCR; + if (m_transmit_buf_count > 4 || dtcr) + { + transmit_value = m_dma_in_cb(m_transmit_buf_addr >> 2, ~0); + if (!dtcr) + { + m_crc32.append(&transmit_value, sizeof(uint32_t)); + } + } + else + { + transmit_value = (uint32_t)m_crc32.finish(); + } + const bool loopback = (m_mode & MODE_LOOP); + if (loopback) + { + // TODO: Differentiate between internal and external loopback + recv_fifo_push(transmit_value); + } + else + { + // TODO: Send data to an actual network interface and/or our real transmit FIFO + } + + if (m_transmit_buf_count >= 4) + { + m_transmit_buf_addr += 4; + m_transmit_buf_count -= 4; + } + else + { + m_transmit_buf_addr += m_transmit_buf_count; + m_transmit_buf_count = 0; + } + + if (m_transmit_buf_count == 0) + { + const uint32_t base_addr = (m_transmit_ring_addr >> 2) + (m_transmit_ring_pos << 1); + ring_descriptor &curr = m_curr_transmit_desc; + curr.m_tmd01 &= ~TMD1_OWN; + + m_dma_out_cb(base_addr, curr.m_tmd01, ~0); + + if (!(curr.m_tmd01 & TMD1_ENP)) + { + fetch_transmit_descriptor(); + m_curr_transmit_desc = m_next_transmit_desc; + + if (!(curr.m_tmd01 & TMD1_OWN)) + { + logerror("%s: LANCE is done transmitting a buffer of this descriptor ring, next ring unowned, resuming polling.\n", machine().describe_context()); + m_transmitting = false; + m_transmit_timer->adjust(attotime::never); + m_transmit_poll_timer->adjust(attotime::from_usec(1600), 0, attotime::from_usec(1600)); + } + else + { + logerror("%s: LANCE is done transmitting a buffer of this descriptor ring, preparing to transmit next buffer.\n", machine().describe_context()); + prepare_transmit_buf(); + } + } + else + { + logerror("%s: LANCE is done transmitting the last buffer of this descriptor ring, raising TINT and resuming polling.\n", machine().describe_context()); + if (m_mode & MODE_LOOP) + { + begin_receiving(); + } + + m_csr[0] |= CSR0_TINT; + update_interrupts(); + m_transmitting = false; + m_transmit_timer->adjust(attotime::never); + m_transmit_poll_timer->adjust(attotime::from_usec(1600), 0, attotime::from_usec(1600)); + } + } +} + +void am79c90_device::prepare_transmit_buf() +{ + ring_descriptor &curr = m_curr_transmit_desc; + m_transmit_buf_addr = ((curr.m_tmd01 << 16) | ((curr.m_tmd01 >> 16) & 0x00ffffff)) | 0xff000000; + const int32_t tmd2 = (int16_t)(curr.m_tmd23 >> 16); + m_transmit_buf_count = (uint16_t)((-tmd2) & 0x00000fff); + if (m_transmit_buf_count == 0) + m_transmit_buf_count = 0x1000; + if (!(m_mode & MODE_DTCR)) + { + m_transmit_buf_count += 4; + } + logerror("%s: LANCE: Valid transmit descriptors found, preparing to transmit %d bytes\n", machine().describe_context(), m_transmit_buf_count); +} + +void am79c90_device::poll_transmit() +{ + ring_descriptor &curr = m_curr_transmit_desc; + const uint32_t base_addr = (m_transmit_ring_addr >> 2) + (m_transmit_ring_pos << 1); + logerror("%s: LANCE polling for packets from %08x\n", machine().describe_context(), base_addr); + curr.m_tmd01 = m_dma_in_cb(base_addr, ~0); + + const uint16_t tmd1 = (uint16_t)curr.m_tmd01; + if (!(tmd1 & TMD1_OWN)) + return; + + if (!(tmd1 & TMD1_STP) && !m_transmitting) + { + // "The STP bit must be set in the first buffer of the packet, or the C-LANCE will skip over this + // descriptor and poll the next descriptor(s) until the OWN and STP bits are set." + m_transmit_ring_pos++; + m_transmit_ring_pos &= m_transmit_ring_length - 1; + logerror("%s: LANCE: No STP on this entry and not transmitting, skipping to next entry\n", machine().describe_context()); + return; + } + + logerror("%s: LANCE: Starting transmitting\n", machine().describe_context()); + + m_transmit_poll_timer->adjust(attotime::never); + m_transmitting = true; + m_crc32.reset(); + + // TMD0's value is retrieved from the value fetched above, but per the AMD Am79C90 manual, page 30: + // "The C-LANCE will read TMD0 and TMD2 to get the rest of the buffer address and the buffer byte count + // when it owns the descriptor. Each of these memory reads is done separately with a new arbitration + // cycle for each transfer." + m_dma_in_cb(base_addr, ~0); + + curr.m_tmd23 = m_dma_in_cb(base_addr + 1, ~0); + + m_transmit_ring_pos++; + m_transmit_ring_pos &= m_transmit_ring_length - 1; + + if (!(tmd1 & TMD1_ENP)) + { + logerror("%s: LANCE: No EOP on this entry, caching next entry and checking ownership\n", machine().describe_context()); + // "BUFFER ERROR is set by the C-LANCE during transmission when the C-LANCE does not find the ENP + // flag in the current buffer and does not own the next buffer." + fetch_transmit_descriptor(); + ring_descriptor &next = m_next_transmit_desc; + + if (!((next.m_tmd01 >> 16) & TMD1_OWN)) + { + logerror("%s: LANCE: No EOP on this entry, but we don't own the next one; setting BUFF\n", machine().describe_context()); + curr.m_tmd23 |= TMD3_BUFF; + m_dma_out_cb(base_addr + 1, curr.m_tmd23, ~0); + m_csr[0] &= ~CSR0_TXON; + m_transmitting = false; + return; + } + } + + prepare_transmit_buf(); + + m_transmit_timer->adjust(attotime::from_hz(10'000'000), 0, attotime::from_hz(10'000'000)); +} + +void am79c90_device::update_interrupts() +{ + if (m_csr[0] & CSR0_ANY_INTR) + { + m_csr[0] |= CSR0_INTR; + } + else + { + m_csr[0] &= ~CSR0_INTR; + } + m_irq_out_cb((m_csr[0] & CSR0_INTR) ? 1 : 0); +} + +READ16_MEMBER(am79c90_device::regs_r) +{ + uint16_t ret = 0; + if (offset) + { + ret = m_rap; + logerror("%s: lance_r: RAP = %04x\n", machine().describe_context(), ret); + } + else + { + ret = m_csr[m_rap]; + logerror("%s: lance_r: CSR%d = %04x\n", machine().describe_context(), m_rap, ret); + } + return ret; +} + +WRITE16_MEMBER(am79c90_device::regs_w) +{ + if (offset) + { + logerror("%s: lance_r: RAP = %d\n", machine().describe_context(), data & 3); + m_rap = data & 3; + } + else + { + logerror("%s: lance_w: CSR%d = %04x\n", machine().describe_context(), m_rap, data); + switch (m_rap) + { + case 0: // Control/Status + m_csr[0] &= ~(data & (CSR0_ANY_ERR | CSR0_IDON)); + if (m_csr[0] & CSR0_ANY_ERR) + m_csr[0] |= CSR0_ERR; + else + m_csr[0] &= ~CSR0_ERR; + if (data & CSR0_STOP) + { + data &= ~(CSR0_RXON | CSR0_TXON | CSR0_TDMD | CSR0_STRT | CSR0_INIT); + m_csr[0] &= ~(CSR0_IDON | CSR0_RXON | CSR0_TXON | CSR0_TDMD | CSR0_STRT | CSR0_INIT); + m_csr[0] |= CSR0_STOP; + m_csr[3] = 0; + m_receive_timer->adjust(attotime::never); + m_transmit_timer->adjust(attotime::never); + m_transmit_poll_timer->adjust(attotime::never); + } + if (data & CSR0_INIT) + { + uint32_t init_addr = 0xff000000 | m_csr[1] | (m_csr[2] << 16); + uint16_t init_block[12]; + + logerror("%s: LANCE Init block:\n", machine().describe_context()); + + for (uint32_t i = 0; i < 6; i++) + { + uint32_t value = m_dma_in_cb((init_addr >> 2) + i, ~0); + init_block[i*2 + 0] = (uint16_t)(value >> 16); + init_block[i*2 + 1] = (uint16_t)value; + logerror("%s: IADR +%02d: %04x\n", machine().describe_context(), i*4, init_block[i*2 + 0]); + logerror("%s: IADR +%02d: %04x\n", machine().describe_context(), i*4 + 2, init_block[i*2 + 1]); + } + + m_mode = init_block[0]; + m_physical_addr = ((uint64_t)init_block[3] << 32) | ((uint64_t)init_block[2] << 16) | (uint64_t)init_block[1]; + m_logical_addr_filter = ((uint64_t)init_block[7] << 48) | ((uint64_t)init_block[6] << 32) + | ((uint64_t)init_block[5] << 16) | (uint64_t)init_block[4]; + m_recv_ring_addr = (((uint32_t)init_block[9] << 16) | (uint32_t)init_block[8]) & 0x00fffff8; + m_recv_ring_addr |= 0xff000000; + m_transmit_ring_addr = (((uint32_t)init_block[11] << 16) | (uint32_t)init_block[10]) & 0x00fffff8; + m_transmit_ring_addr |= 0xff000000; + m_recv_ring_length = 1 << ((init_block[9] >> 13) & 7); + m_transmit_ring_length = 1 << ((init_block[11] >> 13) & 7); + + m_transmit_ring_pos = 0; + m_recv_ring_pos = 0; + + logerror("%s: Mode: %04x\n", machine().describe_context(), m_mode); + logerror("%s: Physical Address: %08x%08x\n", machine().describe_context(), + (uint32_t)(m_physical_addr >> 32), (uint32_t)m_physical_addr); + logerror("%s: Logical Address Filter: %08x%08x\n", machine().describe_context(), + (uint32_t)(m_logical_addr_filter >> 32), (uint32_t)m_logical_addr_filter); + logerror("%s: Receive Ring Address: %08x\n", machine().describe_context(), m_recv_ring_addr); + logerror("%s: Receive Ring Length: %04x\n", machine().describe_context(), m_recv_ring_length); + logerror("%s: Transmit Ring Address: %08x\n", machine().describe_context(), m_transmit_ring_addr); + logerror("%s: Transmit Ring Length: %04x\n", machine().describe_context(), m_transmit_ring_length); + + m_csr[0] &= ~CSR0_STOP; + m_csr[0] |= CSR0_IDON | CSR0_INIT | CSR0_TXON | CSR0_RXON; + + m_receive_timer->adjust(attotime::never); + m_transmit_timer->adjust(attotime::never); + m_transmit_poll_timer->adjust(attotime::never); + } + if (data & CSR0_STRT) + { + m_csr[0] &= ~CSR0_STOP; + if (m_mode & MODE_DRX) + m_csr[0] &= ~CSR0_RXON; + if (m_mode & MODE_DTX) + m_csr[0] &= ~CSR0_TXON; + if (m_csr[0] & CSR0_TXON) + m_transmit_poll_timer->adjust(attotime::from_usec(1600), 0, attotime::from_usec(1600)); + } + update_interrupts(); + if (data & CSR0_TDMD) + { + // TODO: Handle transmit demand + } + break; + case 1: // Least significant 15 bits of the Initialization Block + m_csr[1] = data & 0xfffe; + break; + case 2: // Most significant 8 bits of the Initialization Block + m_csr[2] = data & 0x00ff; + break; + case 3: // Bus master interface + m_csr[3] = data & 0x0007; + break; + } + } +} diff --git a/src/devices/machine/am79c90.h b/src/devices/machine/am79c90.h new file mode 100644 index 00000000000..1f6fcfedd5e --- /dev/null +++ b/src/devices/machine/am79c90.h @@ -0,0 +1,202 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/***************************************************************************** + + AMD Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE) + + TODO: + - Communication with the outside world + - Error handling + - Clocks + +****************************************************************************** + _____ _____ + Vss 1 |* \_/ | 48 Vdd + DAL7 2 | | 47 DAL8 + DAL6 3 | | 46 DAL9 + DAL5 4 | | 45 DAL10 + DAL4 5 | | 44 DAL11 + DAL3 6 | | 43 DAL12 + DAL2 7 | | 42 DAL13 + DAL1 8 | | 41 DAL14 + DAL0 9 | | 40 DAL15 + READ 10 | | 39 A16 + /INTR 11 | | 38 A17 + /DALI 12 | Am79C90 | 37 A18 + /DALI 13 | | 36 A19 + /DAS 14 | | 35 A20 + /BM0,BYTE 15 | | 34 A21 + /BM1,/BUSAKO 16 | | 33 A22 + /HOLD,/BUSRQ 17 | | 32 A23 + ALE,/AS 18 | | 31 RX + /HLDA 19 | | 30 RENA + /CS 20 | | 29 TX + ADR 21 | | 28 CLSN + /READY 22 | | 27 RCLK + /RESET 23 | | 26 TENA + Vss 24 |_____________| 25 TCLK + +**********************************************************************/ + +#ifndef MAME_MACHINE_AM79C90_H +#define MAME_MACHINE_AM79C90_H + +#pragma once + +#include "hashing.h" + +class am79c90_device : public device_t +{ +public: + am79c90_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + + auto dma_out() { return m_dma_out_cb.bind(); } + auto dma_in() { return m_dma_in_cb.bind(); } + auto irq_out() { return m_irq_out_cb.bind(); } + + DECLARE_READ16_MEMBER(regs_r); + DECLARE_WRITE16_MEMBER(regs_w); + +private: + 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; + + static const device_timer_id TIMER_TRANSMIT_POLL = 0; + static const device_timer_id TIMER_TRANSMIT = 1; + //static const device_timer_id TIMER_RECEIVE_POLL = 2; + static const device_timer_id TIMER_RECEIVE = 3; + + enum + { + CSR0_ERR = 0x8000, + CSR0_BABL = 0x4000, + CSR0_CERR = 0x2000, + CSR0_MISS = 0x1000, + CSR0_MERR = 0x0800, + CSR0_RINT = 0x0400, + CSR0_TINT = 0x0200, + CSR0_IDON = 0x0100, + CSR0_INTR = 0x0080, + CSR0_INEA = 0x0040, + CSR0_RXON = 0x0020, + CSR0_TXON = 0x0010, + CSR0_TDMD = 0x0008, + CSR0_STOP = 0x0004, + CSR0_STRT = 0x0002, + CSR0_INIT = 0x0001, + CSR0_ANY_INTR = CSR0_BABL | CSR0_MISS | CSR0_MERR | CSR0_RINT | CSR0_TINT | CSR0_IDON, + CSR0_ANY_ERR = CSR0_BABL | CSR0_CERR | CSR0_MISS | CSR0_MERR, + + CSR3_BSWP = 0x0004, + CSR3_ACON = 0x0002, + CSR3_BCON = 0x0001, + + MODE_DRX = 0x0001, + MODE_DTX = 0x0002, + MODE_LOOP = 0x0004, + MODE_DTCR = 0x0008, + MODE_COLL = 0x0010, + MODE_DRTY = 0x0020, + MODE_INTL = 0x0040, + MODE_EMBA = 0x0080, + MODE_PROM = 0x8000, + + TMD1_ENP = 0x0100, + TMD1_STP = 0x0200, + TMD1_DEF = 0x0400, + TMD1_ONE = 0x0800, + TMD1_MORE = 0x1000, + TMD1_ADD_FCS = 0x2000, + TMD1_ERR = 0x4000, + TMD1_OWN = 0x8000, + + TMD3_RTRY = 0x0400, + TMD3_LCAR = 0x0800, + TMD3_LCOL = 0x1000, + TMD3_RES = 0x2000, + TMD3_UFLO = 0x4000, + TMD3_BUFF = 0x8000, + + RMD1_ENP = 0x0100, + RMD1_STP = 0x0200, + RMD1_BUFF = 0x0400, + RMD1_CRC = 0x0800, + RMD1_OFLO = 0x1000, + RMD1_FRAM = 0x2000, + RMD1_ERR = 0x4000, + RMD1_OWN = 0x8000 + }; + + void prepare_transmit_buf(); + void begin_receiving(); + void recv_fifo_push(uint32_t value); + void fetch_receive_descriptor(); + void fetch_transmit_descriptor(); + void poll_transmit(); + //void poll_receive(); TODO + void transmit(); + void receive(); + void update_interrupts(); + + struct ring_descriptor + { + union + { + uint32_t m_tmd23; + uint32_t m_rmd23; + }; + union + { + uint32_t m_tmd01; + uint32_t m_rmd01; + }; + uint16_t m_byte_count; + uint32_t m_buffer_addr; + }; + + ring_descriptor m_curr_transmit_desc; + ring_descriptor m_next_transmit_desc; + ring_descriptor m_curr_recv_desc; + ring_descriptor m_next_recv_desc; + + uint16_t m_rap; + uint16_t m_csr[4]; + uint16_t m_mode; + uint64_t m_logical_addr_filter; + uint64_t m_physical_addr; + uint32_t m_recv_message_count; + uint32_t m_recv_ring_addr; + uint32_t m_recv_buf_addr; + uint16_t m_recv_buf_count; + uint8_t m_recv_ring_length; + uint8_t m_recv_ring_pos; + uint32_t m_recv_fifo[16]; + uint8_t m_recv_fifo_write; + uint8_t m_recv_fifo_read; + bool m_receiving; + emu_timer *m_receive_timer; + //emu_timer *m_receive_poll_timer; + + uint32_t m_transmit_ring_addr; + uint32_t m_transmit_buf_addr; + uint16_t m_transmit_buf_count; + uint8_t m_transmit_ring_length; + uint8_t m_transmit_ring_pos; + uint32_t m_transmit_fifo[12]; + uint8_t m_transmit_fifo_write; + uint8_t m_transmit_fifo_read; + bool m_transmitting; + emu_timer *m_transmit_timer; + emu_timer *m_transmit_poll_timer; + + devcb_write_line m_irq_out_cb; + devcb_write32 m_dma_out_cb; // TODO: Should be read/write16! + devcb_read32 m_dma_in_cb; + + util::crc32_creator m_crc32; +}; + +DECLARE_DEVICE_TYPE(AM79C90, am79c90_device) + +#endif // MAME_MACHINE_AM79C90_H \ No newline at end of file diff --git a/src/mame/drivers/sun4.cpp b/src/mame/drivers/sun4.cpp index a6ad2be131a..6658c2629ca 100644 --- a/src/mame/drivers/sun4.cpp +++ b/src/mame/drivers/sun4.cpp @@ -414,6 +414,7 @@ #include "bus/rs232/rs232.h" #include "bus/sunkbd/sunkbd.h" #include "cpu/sparc/sparc.h" +#include "machine/am79c90.h" #include "machine/bankdev.h" #include "machine/ncr5390.h" #include "machine/nscsi_bus.h" @@ -434,7 +435,6 @@ #include "formats/mfi_dsk.h" #include "formats/pc_dsk.h" - #define SUN4_LOG_FCODES (0) #define TIMEKEEPER_TAG "timekpr" @@ -444,6 +444,7 @@ #define RS232A_TAG "rs232a" #define RS232B_TAG "rs232b" #define FDC_TAG "fdc" +#define LANCE_TAG "lance" #define ENA_NOTBOOT (0x80) #define ENA_SDVMA (0x20) @@ -540,6 +541,7 @@ public: , m_scc1(*this, SCC1_TAG) , m_scc2(*this, SCC2_TAG) , m_fdc(*this, FDC_TAG) + , m_lance(*this, LANCE_TAG) , m_scsibus(*this, "scsibus") , m_scsi(*this, "scsibus:7:ncr53c90a") , m_type0space(*this, "type0") @@ -584,8 +586,8 @@ private: DECLARE_WRITE8_MEMBER( fdc_w ); DECLARE_READ32_MEMBER( dma_r ); DECLARE_WRITE32_MEMBER( dma_w ); - DECLARE_READ16_MEMBER( lance_r ); - DECLARE_WRITE16_MEMBER( lance_w ); + DECLARE_READ32_MEMBER( lance_dma_r ); // TODO: Should be 16 bits + DECLARE_WRITE32_MEMBER( lance_dma_w ); DECLARE_WRITE_LINE_MEMBER( scsi_irq ); DECLARE_WRITE_LINE_MEMBER( scsi_drq ); @@ -605,6 +607,12 @@ private: void type1space_map(address_map &map); void type1space_s4_map(address_map &map); + enum sun4_arch + { + SUN4 = 0, + SUN4C = 1 + }; + required_device m_maincpu; required_device m_timekpr; @@ -613,6 +621,7 @@ private: required_device m_scc2; required_device m_fdc; + required_device m_lance; required_device m_scsibus; required_device m_scsi; @@ -644,9 +653,6 @@ private: uint8_t m_diag; int m_arch; - uint16_t m_lance_rap; - uint16_t m_lance_csr[4]; - emu_timer *m_c0_timer, *m_c1_timer; emu_timer *m_reset_timer; @@ -783,7 +789,7 @@ void sun4_state::write_insn_data_4c(uint8_t asi, address_space &space, uint32_t } else { - printf("sun4c: INVALID PTE entry %d %08x accessed! vaddr=%x PC=%x\n", entry, m_pagemap[entry], offset <<2, m_maincpu->pc()); + printf("sun4c: INVALID PTE entry %d %08x accessed! data=%08x vaddr=%x PC=%x\n", entry, m_pagemap[entry], data, offset <<2, m_maincpu->pc()); //m_maincpu->trap(SPARC_DATA_ACCESS_EXCEPTION); //m_buserr[0] = 0x8; // invalid PTE //m_buserr[1] = offset<<2; @@ -910,7 +916,7 @@ WRITE32_MEMBER( sun4_state::sun4c_mmu_w ) { m_reset_timer->adjust(attotime::from_usec(1)); m_maincpu->set_input_line(SPARC_RESET, ASSERT_LINE); - //printf("Asserting reset line\n"); + logerror("%s: Asserting reset line\n", machine().describe_context()); } //printf("%08x to system enable, mask %08x\n", data, mem_mask); if (m_system_enable & ENA_RESET) @@ -1066,7 +1072,7 @@ void sun4_state::write_insn_data(uint8_t asi, address_space &space, uint32_t off } else { - printf("sun4: INVALID PTE entry %d %08x accessed! vaddr=%x PC=%x\n", entry, m_pagemap[entry], offset <<2, m_maincpu->pc()); + printf("sun4: INVALID PTE entry %d %08x accessed! data=%08x vaddr=%x PC=%x\n", entry, m_pagemap[entry], data, offset <<2, m_maincpu->pc()); //m_maincpu->trap(SPARC_DATA_ACCESS_EXCEPTION); //m_buserr[0] = 0x8; // invalid PTE //m_buserr[1] = offset<<2; @@ -1191,6 +1197,7 @@ WRITE32_MEMBER( sun4_state::sun4_mmu_w ) { m_reset_timer->adjust(attotime::from_usec(1)); m_maincpu->set_input_line(SPARC_RESET, ASSERT_LINE); + logerror("%s: Asserting reset line\n", machine().describe_context()); } //printf("%08x to system enable, mask %08x\n", data, mem_mask); if (m_system_enable & ENA_RESET) @@ -1352,31 +1359,6 @@ void sun4_state::sun4c_mem(address_map &map) static INPUT_PORTS_START( sun4 ) INPUT_PORTS_END -enum -{ - LANCE_CSR0_ERR = 0x8000, - LANCE_CSR0_BABL = 0x4000, - LANCE_CSR0_CERR = 0x2000, - LANCE_CSR0_MISS = 0x1000, - LANCE_CSR0_MERR = 0x0800, - LANCE_CSR0_RINT = 0x0400, - LANCE_CSR0_TINT = 0x0200, - LANCE_CSR0_IDON = 0x0100, - LANCE_CSR0_INTR = 0x0080, - LANCE_CSR0_INEA = 0x0040, - LANCE_CSR0_RXON = 0x0020, - LANCE_CSR0_TXON = 0x0010, - LANCE_CSR0_TDMD = 0x0008, - LANCE_CSR0_STOP = 0x0004, - LANCE_CSR0_STRT = 0x0002, - LANCE_CSR0_INIT = 0x0001, - LANCE_CSR0_ANY_ERR = LANCE_CSR0_BABL | LANCE_CSR0_CERR | LANCE_CSR0_MISS | LANCE_CSR0_MERR, - - LANCE_CSR3_BSWP = 0x0004, - LANCE_CSR3_ACON = 0x0002, - LANCE_CSR3_BCON = 0x0001 -}; - void sun4_state::machine_reset() { m_context = 0; @@ -1387,10 +1369,6 @@ void sun4_state::machine_reset() m_dma_tc_read = false; m_dma_pack_register = 0; - m_lance_rap = 0; - memset(m_lance_csr, 0, sizeof(uint16_t) * 4); - m_lance_csr[0] = LANCE_CSR0_STOP; - memset(m_counter, 0, sizeof(m_counter)); memset(m_dma, 0, sizeof(m_dma)); } @@ -1514,7 +1492,7 @@ void sun4_state::type1space_map(address_map &map) map(0x08000000, 0x08000003).r(FUNC(sun4_state::ss1_sl0_id)); // slot 0 contains SCSI/DMA/Ethernet map(0x08400000, 0x0840000f).rw(FUNC(sun4_state::dma_r), FUNC(sun4_state::dma_w)); map(0x08800000, 0x0880001f).m(m_scsi, FUNC(ncr53c90a_device::map)).umask32(0xff000000); - map(0x08c00000, 0x08c00003).rw(FUNC(sun4_state::lance_r), FUNC(sun4_state::lance_w)); + map(0x08c00000, 0x08c00003).rw(m_lance, FUNC(am79c90_device::regs_r), FUNC(am79c90_device::regs_w)); map(0x0e000000, 0x0e000003).r(FUNC(sun4_state::ss1_sl3_id)); // slot 3 contains video board map(0x0e800000, 0x0e8fffff).ram().share("bw2_vram"); } @@ -1743,7 +1721,7 @@ void sun4_state::dma_transfer_write() logerror("Read from device: %02x, pack count %d\n", dma_value, pack_cnt); m_dma_pack_register |= dma_value << bit_index; - if ((m_dma[DMA_CTRL] & DMA_EN_CNT) != 0) + //if (m_dma[DMA_CTRL] & DMA_EN_CNT) m_dma[DMA_BYTE_COUNT]--; pack_cnt++; @@ -1802,7 +1780,7 @@ void sun4_state::dma_transfer_read() word_cached = false; m_dma[DMA_ADDR]++; - if ((m_dma[DMA_CTRL] & DMA_EN_CNT) != 0) + //if (m_dma[DMA_CTRL] & DMA_EN_CNT) m_dma[DMA_BYTE_COUNT]--; } } @@ -1818,7 +1796,7 @@ void sun4_state::dma_transfer() dma_transfer_read(); } - if (m_dma[DMA_BYTE_COUNT] == 0) + if (m_dma[DMA_BYTE_COUNT] == 0 && (m_dma[DMA_CTRL] & DMA_EN_CNT)) { m_dma[DMA_CTRL] |= DMA_TC; m_dma_tc_read = false; @@ -1911,7 +1889,7 @@ WRITE_LINE_MEMBER( sun4_state::scsi_drq ) { logerror("scsi_drq, DMA pending\n"); m_dma[DMA_CTRL] |= DMA_REQ_PEND; - if (m_dma[DMA_CTRL] & DMA_EN_DMA) + if (m_dma[DMA_CTRL] & DMA_EN_DMA && m_dma[DMA_BYTE_COUNT]) { logerror("DMA enabled, starting dma\n"); dma_transfer(); @@ -1919,6 +1897,22 @@ WRITE_LINE_MEMBER( sun4_state::scsi_drq ) } } +READ32_MEMBER( sun4_state::lance_dma_r ) +{ + if (m_arch == ARCH_SUN4) + return read_insn_data(11, m_maincpu->space(AS_PROGRAM), offset, mem_mask); + else + return read_insn_data_4c(11, m_maincpu->space(AS_PROGRAM), offset, mem_mask); +} + +WRITE32_MEMBER( sun4_state::lance_dma_w ) +{ + if (m_arch == ARCH_SUN4) + write_insn_data(11, m_maincpu->space(AS_PROGRAM), offset, data, mem_mask); + else + write_insn_data_4c(11, m_maincpu->space(AS_PROGRAM), offset, data, mem_mask); +} + // indicate 4/60 SCSI/DMA/Ethernet card exists READ32_MEMBER( sun4_state::ss1_sl0_id ) { @@ -1931,75 +1925,6 @@ READ32_MEMBER( sun4_state::ss1_sl3_id ) return 0xfe010101; } -READ16_MEMBER( sun4_state::lance_r ) -{ - uint16_t ret = 0; - if (offset) - { - ret = m_lance_rap; - logerror("%s: lance_r: RAP = %04x\n", machine().describe_context(), ret); - } - else - { - ret = m_lance_csr[m_lance_rap]; - logerror("%s: lance_r: CSR%d = %04x\n", machine().describe_context(), m_lance_rap, ret); - } - return ret; -} - -WRITE16_MEMBER( sun4_state::lance_w ) -{ - if (offset) - { - logerror("%s: lance_r: RAP = %d\n", machine().describe_context(), data & 3); - m_lance_rap = data & 3; - } - else - { - logerror("%s: lance_w: CSR%d = %04x\n", machine().describe_context(), m_lance_rap, data); - switch (m_lance_rap) - { - case 0: // Control/Status - m_lance_csr[0] &= ~(data & (LANCE_CSR0_ANY_ERR | LANCE_CSR0_IDON)); - if (m_lance_csr[0] & LANCE_CSR0_ANY_ERR) - m_lance_csr[0] |= LANCE_CSR0_ERR; - else - m_lance_csr[0] &= ~LANCE_CSR0_ERR; - if (data & LANCE_CSR0_STOP) - { - data &= ~(LANCE_CSR0_RXON | LANCE_CSR0_TXON | LANCE_CSR0_TDMD | LANCE_CSR0_STRT | LANCE_CSR0_INIT); - m_lance_csr[0] &= ~(LANCE_CSR0_IDON | LANCE_CSR0_RXON | LANCE_CSR0_TXON | LANCE_CSR0_TDMD | LANCE_CSR0_STRT | LANCE_CSR0_INIT); - m_lance_csr[3] = 0; - } - if (data & LANCE_CSR0_INIT) - { - // TODO: Actually parse initialization block - m_lance_csr[0] &= ~LANCE_CSR0_STOP; - m_lance_csr[0] |= LANCE_CSR0_IDON | LANCE_CSR0_INIT; - } - if (data & LANCE_CSR0_STRT) - { - m_lance_csr[0] &= ~LANCE_CSR0_STOP; - m_lance_csr[0] |= (LANCE_CSR0_RXON | LANCE_CSR0_TXON | LANCE_CSR0_TINT | LANCE_CSR0_RINT); // TODO: Handle DTX/DRX in initialization block - } - if (data & (LANCE_CSR0_BABL | LANCE_CSR0_MISS | LANCE_CSR0_MERR | LANCE_CSR0_RINT | LANCE_CSR0_TINT | LANCE_CSR0_IDON)) - { - m_lance_csr[0] |= LANCE_CSR0_INTR; - } - break; - case 1: // Least significant 15 bits of the Initialization Block - m_lance_csr[1] = data & 0xfffe; - break; - case 2: // Most significant 8 bits of the Initialization Block - m_lance_csr[2] = data & 0x00ff; - break; - case 3: // Bus master interface - m_lance_csr[3] = data & 0x0007; - break; - } - } -} - FLOPPY_FORMATS_MEMBER( sun4_state::floppy_formats ) FLOPPY_PC_FORMAT FLOPPY_FORMATS_END @@ -2044,6 +1969,11 @@ MACHINE_CONFIG_START(sun4_state::sun4) // MMU Type 1 device space ADDRESS_MAP_BANK(config, "type1").set_map(&sun4_state::type1space_s4_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000); + // Ethernet + AM79C90(config, m_lance, 10'000'000); // clock is a guess + m_lance->dma_in().set(FUNC(sun4_state::lance_dma_r)); + m_lance->dma_out().set(FUNC(sun4_state::lance_dma_w)); + // Keyboard/mouse SCC8530N(config, m_scc1, 4.9152_MHz_XTAL); m_scc1->out_int_callback().set(FUNC(sun4_state::scc1_int)); @@ -2099,6 +2029,11 @@ MACHINE_CONFIG_START(sun4_state::sun4c) // MMU Type 1 device space ADDRESS_MAP_BANK(config, "type1").set_map(&sun4_state::type1space_map).set_options(ENDIANNESS_BIG, 32, 32, 0x80000000); + // Ethernet + AM79C90(config, m_lance, 10'000'000); // clock is a guess + m_lance->dma_in().set(FUNC(sun4_state::lance_dma_r)); + m_lance->dma_out().set(FUNC(sun4_state::lance_dma_w)); + // Keyboard/mouse SCC8530N(config, m_scc1, 4.9152_MHz_XTAL); m_scc1->out_int_callback().set(FUNC(sun4_state::scc1_int));