diff options
Diffstat (limited to 'src/devices/machine/smc91c9x.cpp')
-rw-r--r-- | src/devices/machine/smc91c9x.cpp | 1141 |
1 files changed, 601 insertions, 540 deletions
diff --git a/src/devices/machine/smc91c9x.cpp b/src/devices/machine/smc91c9x.cpp index e081c2c6a17..359a69eefdb 100644 --- a/src/devices/machine/smc91c9x.cpp +++ b/src/devices/machine/smc91c9x.cpp @@ -4,92 +4,29 @@ SMC91C9X ethernet controller implementation - by Aaron Giles, Jean-François DEL NERO + by Aaron Giles, Ted Green -*************************************************************************** - - Notes: - * Connected mode working - -**************************************************************************/ +***************************************************************************/ #include "emu.h" #include "smc91c9x.h" -// Needed for netdev_count??? -#include "osdnet.h" - +#include <sstream> +#include <iomanip> /*************************************************************************** DEBUGGING ***************************************************************************/ -//#define VERBOSE 1 +#define LOG_GENERAL (1U << 0) +#define LOG_PACKETS (1U << 1) +#define LOG_TX (1U << 2) +#define LOG_RX (1U << 3) +#define LOG_FILTER (1U << 4) +//#define VERBOSE (LOG_GENERAL | LOG_PACKETS | LOG_TX | LOG_RX | LOG_FILTER) #include "logmacro.h" #define DISPLAY_STATS (0) - - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -/* Ethernet registers - bank 0 */ -#define EREG_TCR (0*8 + 0) -#define EREG_EPH_STATUS (0*8 + 1) -#define EREG_RCR (0*8 + 2) -#define EREG_COUNTER (0*8 + 3) -#define EREG_MIR (0*8 + 4) -#define EREG_MCR (0*8 + 5) -#define EREG_BANK (0*8 + 7) - -/* Ethernet registers - bank 1 */ -#define EREG_CONFIG (1*8 + 0) -#define EREG_BASE (1*8 + 1) -#define EREG_IA0_1 (1*8 + 2) -#define EREG_IA2_3 (1*8 + 3) -#define EREG_IA4_5 (1*8 + 4) -#define EREG_GENERAL_PURP (1*8 + 5) -#define EREG_CONTROL (1*8 + 6) - -/* Ethernet registers - bank 2 */ -#define EREG_MMU_COMMAND (2*8 + 0) -#define EREG_PNR_ARR (2*8 + 1) -#define EREG_FIFO_PORTS (2*8 + 2) -#define EREG_POINTER (2*8 + 3) -#define EREG_DATA_0 (2*8 + 4) -#define EREG_DATA_1 (2*8 + 5) -#define EREG_INTERRUPT (2*8 + 6) - -/* Ethernet registers - bank 3 */ -#define EREG_MT0_1 (3*8 + 0) -#define EREG_MT2_3 (3*8 + 1) -#define EREG_MT4_5 (3*8 + 2) -#define EREG_MT6_7 (3*8 + 3) -#define EREG_MGMT (3*8 + 4) -#define EREG_REVISION (3*8 + 5) -#define EREG_ERCV (3*8 + 6) - -/* Ethernet MMU commands */ -#define ECMD_NOP 0 -#define ECMD_ALLOCATE 2 -#define ECMD_RESET_MMU 4 -#define ECMD_REMOVE_TOPFRAME_RX 6 -#define ECMD_REMOVE_TOPFRAME_TX 7 -#define ECMD_REMOVE_RELEASE_TOPFRAME_RX 8 -#define ECMD_RELEASE_PACKET 10 -#define ECMD_ENQUEUE_PACKET 12 -#define ECMD_RESET_FIFOS 14 - -/* Ethernet interrupt bits */ -#define EINT_RCV 0x01 -#define EINT_TX 0x02 -#define EINT_TX_EMPTY 0x04 -#define EINT_ALLOC 0x08 -#define EINT_RX_OVRN 0x10 -#define EINT_EPH 0x20 -#define EINT_ERCV 0x40 - /* Ethernet register names */ static const char *const ethernet_regname[64] = { @@ -107,73 +44,93 @@ static const char *const ethernet_regname[64] = DEVICE INTERFACE ***************************************************************************/ -smc91c9x_device::smc91c9x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) +DEFINE_DEVICE_TYPE(SMC91C94, smc91c94_device, "smc91c94", "SMC91C94 Ethernet Controller") + +smc91c94_device::smc91c94_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : smc91c9x_device(mconfig, SMC91C94, tag, owner, clock, dev_type::SMC91C94) +{ + m_num_ebuf = 18; +} + +DEFINE_DEVICE_TYPE(SMC91C96, smc91c96_device, "smc91c96", "SMC91C96 Ethernet Controller") + +smc91c96_device::smc91c96_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : smc91c9x_device(mconfig, SMC91C96, tag, owner, clock, dev_type::SMC91C96) +{ + m_num_ebuf = 24; +} + +smc91c9x_device::smc91c9x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, dev_type device_type) : device_t(mconfig, type, tag, owner, clock) , device_network_interface(mconfig, *this, 10.0f) + , m_device_type(device_type) + , m_num_ebuf(16) , m_irq_handler(*this) , m_link_unconnected(false) { } +const u8 smc91c9x_device::ETH_BROADCAST[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +const u8 smc91c9x_device::WMS_OUI[] = { 0x00, 0xA0, 0xAF }; + //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void smc91c9x_device::device_start() { + // Allocate main buffer + m_buffer = std::make_unique<u8[]>(ETHER_BUFFER_SIZE * m_num_ebuf); + // TX timer - m_tx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(smc91c9x_device::send_frame), this)); + m_tx_poll = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(smc91c9x_device::tx_poll), this)); m_irq_handler.resolve_safe(); + // These registers don't get cleared on reset + m_reg[B1_CONFIG] = 0x0030; m_regmask[B1_CONFIG] = 0x17c6; + m_reg[B1_BASE] = 0x1866; m_regmask[B1_BASE] = 0xfffe; + + m_reg[B1_IA0_1] = 0x0000; m_regmask[B1_IA0_1] = 0xffff; + m_reg[B1_IA2_3] = 0x0000; m_regmask[B1_IA2_3] = 0xffff; + m_reg[B1_IA4_5] = 0x0000; m_regmask[B1_IA4_5] = 0xffff; + + // Revision is set based on chip type + m_regmask[B3_REVISION] = 0x0000; + if (m_device_type == dev_type::SMC91C94) + m_reg[B3_REVISION] = 0x3345; + else if (m_device_type == dev_type::SMC91C96) + m_reg[B3_REVISION] = 0x3346; + else + fatalerror("device_start: Unknown device type\n"); + /* register ide states */ save_item(NAME(m_reg)); save_item(NAME(m_regmask)); save_item(NAME(m_irq_state)); - save_item(NAME(m_buffer)); + save_pointer(NAME(m_buffer), ETHER_BUFFER_SIZE * m_num_ebuf); save_item(NAME(m_sent)); save_item(NAME(m_recd)); save_item(NAME(m_alloc_rx)); save_item(NAME(m_alloc_tx)); - // Save vector data for proper save state restoration - save_item(NAME(m_comp_rx_data)); - save_item(NAME(m_comp_tx_data)); - save_item(NAME(m_trans_tx_data)); - // Save vector sizes for proper save state restoration - save_item(NAME(m_comp_tx_size)); - save_item(NAME(m_comp_rx_size)); - save_item(NAME(m_trans_tx_size)); -} - -// Save state presave to save vector sizes -void smc91c9x_device::device_pre_save() -{ - m_comp_tx_size = m_comp_tx.size(); - m_comp_rx_size = m_comp_rx.size(); - m_trans_tx_size = m_trans_tx.size(); - memcpy(m_comp_rx_data, m_comp_rx.data(), m_comp_rx_size * sizeof(u32)); - memcpy(m_comp_tx_data, m_comp_tx.data(), m_comp_tx_size * sizeof(u32)); - memcpy(m_trans_tx_data, m_trans_tx.data(), m_trans_tx_size * sizeof(u32)); - - //osd_printf_info("Save: comp_tx: %d comp_rx: %d trans_tx: %d\n", m_comp_tx_size, m_comp_rx_size, m_trans_tx_size); - //if (m_comp_tx_size) - // osd_printf_info("comp_tx packet: %d\n", m_comp_tx.front()); + save_item(NAME(m_tx_active)); + save_item(NAME(m_rx_active)); + save_item(NAME(m_tx_retry_count)); + save_item(NAME(m_rx_hash)); + save_item(NAME(m_loopback_result)); + + // Circular FIFOs + save_item(NAME(m_queued_tx)); + save_item(NAME(m_queued_tx_h)); + save_item(NAME(m_queued_tx_t)); + save_item(NAME(m_completed_tx)); + save_item(NAME(m_completed_tx_h)); + save_item(NAME(m_completed_tx_t)); + save_item(NAME(m_completed_rx)); + save_item(NAME(m_completed_rx_h)); + save_item(NAME(m_completed_rx_t)); } -// Save state preload to restore vector sizes -void smc91c9x_device::device_post_load() -{ - m_comp_tx.resize(m_comp_tx_size); - m_comp_rx.resize(m_comp_rx_size); - m_trans_tx.resize(m_trans_tx_size); - memcpy(m_comp_rx.data(), m_comp_rx_data, m_comp_rx_size * sizeof(u32)); - memcpy(m_comp_tx.data(), m_comp_tx_data, m_comp_tx_size * sizeof(u32)); - memcpy(m_trans_tx.data(), m_trans_tx_data, m_trans_tx_size * sizeof(u32)); - - //osd_printf_info("Restore: comp_tx: %d comp_rx: %d trans_tx: %d\n", m_comp_tx_size, m_comp_rx_size, m_trans_tx_size); - //if (m_comp_tx_size) - // osd_printf_info("comp_tx size: %lu comp_tx packet: %d array_data: %d\n", m_comp_tx.size(), m_comp_tx.front(), m_comp_tx_data[0]); -} //------------------------------------------------- // device_reset - device-specific reset @@ -181,75 +138,43 @@ void smc91c9x_device::device_post_load() void smc91c9x_device::device_reset() { - std::fill(std::begin(m_reg), std::end(m_reg), 0); - - std::fill(std::begin(m_regmask), std::end(m_regmask), 0); - m_irq_state = 0; m_sent = 0; m_recd = 0; - m_reg[EREG_TCR] = 0x0000; m_regmask[EREG_TCR] = 0x3d87; - m_reg[EREG_EPH_STATUS] = 0x0000; m_regmask[EREG_EPH_STATUS] = 0x0000; - m_reg[EREG_RCR] = 0x0000; m_regmask[EREG_RCR] = 0xc307; - m_reg[EREG_COUNTER] = 0x0000; m_regmask[EREG_COUNTER] = 0x0000; - m_reg[EREG_MIR] = 0x1212; m_regmask[EREG_MIR] = 0x0000; - m_reg[EREG_MCR] = 0x3300; m_regmask[EREG_MCR] = 0x00ff; - m_reg[EREG_BANK] = 0x3300; m_regmask[EREG_BANK] = 0x0007; - - m_reg[EREG_CONFIG] = 0x0030; m_regmask[EREG_CONFIG] = 0x17c6; - m_reg[EREG_BASE] = 0x1866; m_regmask[EREG_BASE] = 0xfffe; - - // Default MAC - m_reg[EREG_IA0_1] = 0x1300; m_regmask[EREG_IA0_1] = 0xffff; - m_reg[EREG_IA2_3] = 0x12F7; m_regmask[EREG_IA2_3] = 0xffff; - m_reg[EREG_IA4_5] = 0x5634; m_regmask[EREG_IA4_5] = 0xffff; - - m_reg[EREG_GENERAL_PURP] = 0x0000; m_regmask[EREG_GENERAL_PURP] = 0xffff; - m_reg[EREG_CONTROL] = 0x0100; m_regmask[EREG_CONTROL] = 0x68e7; - - m_reg[EREG_MMU_COMMAND] = 0x0000; m_regmask[EREG_MMU_COMMAND] = 0x00e7; - m_reg[EREG_PNR_ARR] = 0x8000; m_regmask[EREG_PNR_ARR] = 0x00ff; - m_reg[EREG_FIFO_PORTS] = 0x8080; m_regmask[EREG_FIFO_PORTS] = 0x0000; - m_reg[EREG_POINTER] = 0x0000; m_regmask[EREG_POINTER] = 0xf7ff; - m_reg[EREG_DATA_0] = 0x0000; m_regmask[EREG_DATA_0] = 0xffff; - m_reg[EREG_DATA_1] = 0x0000; m_regmask[EREG_DATA_1] = 0xffff; - m_reg[EREG_INTERRUPT] = 0x0004; m_regmask[EREG_INTERRUPT] = 0x7f00; - - m_reg[EREG_MT0_1] = 0x0000; m_regmask[EREG_MT0_1] = 0xffff; - m_reg[EREG_MT2_3] = 0x0000; m_regmask[EREG_MT2_3] = 0xffff; - m_reg[EREG_MT4_5] = 0x0000; m_regmask[EREG_MT4_5] = 0xffff; - m_reg[EREG_MT6_7] = 0x0000; m_regmask[EREG_MT6_7] = 0xffff; - m_reg[EREG_MGMT] = 0x3030; m_regmask[EREG_MGMT] = 0x0f0f; - // TODO: Revision should be set based on chip type - m_reg[EREG_REVISION] = 0x3345; m_regmask[EREG_REVISION] = 0x0000; - m_reg[EREG_ERCV] = 0x331f; m_regmask[EREG_ERCV] = 0x009f; + m_tx_active = 0; + m_rx_active = 0; + m_tx_retry_count = 0; + + m_reg[B0_TCR] = 0x0000; m_regmask[B0_TCR] = 0x3d87; + m_reg[B0_EPH_STATUS] = 0x0000; m_regmask[B0_EPH_STATUS] = 0x0000; + m_reg[B0_RCR] = 0x0000; m_regmask[B0_RCR] = 0xc307; + m_reg[B0_COUNTER] = 0x0000; m_regmask[B0_COUNTER] = 0x0000; + m_reg[B0_MIR] = 0x1212; m_regmask[B0_MIR] = 0x0000; + m_reg[B0_MCR] = 0x3300; m_regmask[B0_MCR] = 0x00ff; + m_reg[B0_BANK] = 0x3300; m_regmask[B0_BANK] = 0x0007; + + m_reg[B1_GENERAL_PURP] = 0x0000; m_regmask[B1_GENERAL_PURP] = 0xffff; + m_reg[B1_CONTROL] = 0x0100; m_regmask[B1_CONTROL] = 0x68e7; + + m_reg[B2_MMU_COMMAND] = 0x0000; m_regmask[B2_MMU_COMMAND] = 0x00e7; + m_reg[B2_PNR_ARR] = 0x8000; m_regmask[B2_PNR_ARR] = 0x00ff; + m_reg[B2_FIFO_PORTS] = 0x8080; m_regmask[B2_FIFO_PORTS] = 0x0000; + m_reg[B2_POINTER] = 0x0000; m_regmask[B2_POINTER] = 0xf7ff; + m_reg[B2_DATA_0] = 0x0000; m_regmask[B2_DATA_0] = 0xffff; + m_reg[B2_DATA_1] = 0x0000; m_regmask[B2_DATA_1] = 0xffff; + m_reg[B2_INTERRUPT] = 0x0004; m_regmask[B2_INTERRUPT] = 0x7f00; + + m_reg[B3_MT0_1] = 0x0000; m_regmask[B3_MT0_1] = 0xffff; + m_reg[B3_MT2_3] = 0x0000; m_regmask[B3_MT2_3] = 0xffff; + m_reg[B3_MT4_5] = 0x0000; m_regmask[B3_MT4_5] = 0xffff; + m_reg[B3_MT6_7] = 0x0000; m_regmask[B3_MT6_7] = 0xffff; + m_reg[B3_MGMT] = 0x3030; m_regmask[B3_MGMT] = 0x0f0f; + + m_reg[B3_ERCV] = 0x331f; m_regmask[B3_ERCV] = 0x009f; update_ethernet_irq(); - m_tx_timer->reset(); - - // Setup real network if enabled - m_network_available = false; - if (netdev_count()) { - m_network_available = true; - osd_list_network_adapters(); - unsigned char const *const mac = (const unsigned char *)get_mac(); - if (VERBOSE & LOG_GENERAL) - { - logerror("MAC : "); - for (int i = 0; i < ETHERNET_ADDR_SIZE; i++) - logerror("%.2X", mac[i]); - - logerror("\n"); - } - - set_promisc(true); - // Interface MAC - m_reg[EREG_IA0_1] = mac[0] | (mac[1] << 8); - m_reg[EREG_IA2_3] = mac[2] | (mac[3] << 8); - m_reg[EREG_IA4_5] = mac[4] | (mac[5] << 8); - } // Reset MMU mmu_reset(); @@ -261,39 +186,36 @@ void smc91c9x_device::mmu_reset() m_alloc_rx = 0; m_alloc_tx = 0; - // Flush fifos. - clear_tx_fifo(); - clear_rx_fifo(); + // Reset fifos. + reset_tx_fifos(); + reset_completed_rx(); update_ethernet_irq(); } -DEFINE_DEVICE_TYPE(SMC91C94, smc91c94_device, "smc91c94", "SMC91C94 Ethernet Controller") - -smc91c94_device::smc91c94_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : smc91c9x_device(mconfig, SMC91C94, tag, owner, clock) -{ -} - -DEFINE_DEVICE_TYPE(SMC91C96, smc91c96_device, "smc91c96", "SMC91C96 Ethernet Controller") - -smc91c96_device::smc91c96_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : smc91c9x_device(mconfig, SMC91C96, tag, owner, clock) +void smc91c9x_device::reset_tx_fifos() { + // Disable transmit timer + m_tx_poll->enable(false); + // Reset transmit queue + reset_queued_tx(); + // Reset completion FIFOs + reset_completed_tx(); } bool smc91c9x_device::alloc_req(const int tx, int &packet_num) { u32 curr_alloc = m_alloc_rx | m_alloc_tx; - for (int index = 0; index < ETHER_BUFFERS; index++) { - if (!(curr_alloc & (1 << index))) { + for (int index = 0; index < m_num_ebuf; index++) + { + if (!(curr_alloc & (1 << index))) + { packet_num = index; - if (tx) { + if (tx) m_alloc_tx |= 1 << index; - } else { + else m_alloc_rx |= 1 << index; - } return true; } } @@ -304,7 +226,8 @@ bool smc91c9x_device::alloc_req(const int tx, int &packet_num) void smc91c9x_device::alloc_release(const int packet_num) { int clear_mask = ~(1 << packet_num); - if (!((m_alloc_tx | m_alloc_rx) & (1 << packet_num))) { + if (!((m_alloc_tx | m_alloc_rx) & (1 << packet_num))) + { logerror("alloc_release: Trying to release a non-allocated packet. packet_num: %02x alloc_tx: %04x alloc_rx: %04x\n", packet_num, m_alloc_tx, m_alloc_rx); } @@ -312,338 +235,448 @@ void smc91c9x_device::alloc_release(const int packet_num) m_alloc_rx &= clear_mask; } -void smc91c9x_device::clear_tx_fifo() -{ - // Clear transmit timer - m_tx_timer->reset(); - // Reset transmit queue - m_trans_tx.clear(); - // Reset completion FIFOs - m_comp_tx.clear(); -} +/*************************************************************************** + INTERNAL HELPERS +***************************************************************************/ -void smc91c9x_device::clear_rx_fifo() -{ - // Clear recieve FIFO - m_comp_rx.clear(); -} +/*------------------------------------------------- + update_ethernet_irq - update the IRQ state +-------------------------------------------------*/ -int smc91c9x_device::is_broadcast(const uint8_t *mac_address) +void smc91c9x_device::update_ethernet_irq() { - int i; + // Check tx completion fifo empty + if (empty_completed_tx()) + m_reg[B2_INTERRUPT] &= ~EINT_TX; + else + m_reg[B2_INTERRUPT] |= EINT_TX; + + // Check rx completion fifo empty + if (empty_completed_rx()) + m_reg[B2_INTERRUPT] &= ~EINT_RCV; + else + m_reg[B2_INTERRUPT] |= EINT_RCV; + + uint8_t const mask = m_reg[B2_INTERRUPT] >> 8; + uint8_t const state = m_reg[B2_INTERRUPT] & 0xff; - i = 0; - while(mac_address[i] == 0xFF) + /* update the IRQ state */ + uint8_t new_state = mask & state; + if (m_irq_state ^ new_state) { - i++; + LOG("update_ethernet_irq: old: %02x new: %02x\n", m_irq_state, new_state); + m_irq_state = new_state; + m_irq_handler(m_irq_state ? ASSERT_LINE : CLEAR_LINE); } +} - if ( i == 6 ) - return 1; - return 0; +/*------------------------------------------------- + update_stats - draw statistics +-------------------------------------------------*/ + +void smc91c9x_device::update_stats() +{ + if ( DISPLAY_STATS ) + popmessage("Sent:%d Rec'd:%d", m_sent, m_recd); } +/*------------------------------------------------- +dump_bytes - Print packet bytes +-------------------------------------------------*/ -int smc91c9x_device::ethernet_packet_is_for_me(const uint8_t *mac_address) +void smc91c9x_device::dump_bytes(u8 *buf, int length) { - // tcpdump -i eth0 -q ether host 08:00:1e:01:ae:a5 or ether broadcast or ether dst 09:00:1e:00:00:00 or ether dst 09:00:1e:00:00:01 - // wireshark filter: eth.addr eq 08:00:1e:01:ae:a5 or eth.dst eq ff:ff:ff:ff:ff:ff or eth.dst eq 09:00:1e:00:00:00 or eth.dst eq 09:00:1e:00:00:01 - - int i; + if (VERBOSE & LOG_PACKETS) + { + std::stringstream ss_bytes; + ss_bytes << std::hex << std::setfill('0'); + for (int i = 0; i < length; i++) + { + ss_bytes << std::setw(2) << (int) buf[i]; + // Send newline every 16 bytes and at the end + if ((i & 0xf) == 0xf || i == length - 1) + { + LOGMASKED(LOG_PACKETS, "%s\n", ss_bytes.str()); + ss_bytes.str(""); + } + else + ss_bytes << " "; + } + } +} - LOG("\n"); +/*------------------------------------------------- +address_filter - Filter the received packet +-------------------------------------------------*/ - if (VERBOSE & LOG_GENERAL) +int smc91c9x_device::address_filter(u8 *buf) +{ + if (m_reg[B0_RCR] & PRMS) + { + // TODO: 91C94 doesn't receive it's own transmisson when not in full duplex + LOGMASKED(LOG_FILTER, "address_filter accepted (promiscuous mode)\n"); + return ADDR_UNICAST; + } + else if (buf[0] & 1) { - for ( i = 0 ; i < ETHERNET_ADDR_SIZE ; i++ ) + // broadcast + if (!memcmp(ETH_BROADCAST, buf, 6)) { - logerror("%.2X", ((u8 *)&m_reg[EREG_IA0_1])[i]); + LOGMASKED(LOG_FILTER, "address_filter accepted (broadcast) %02x-%02x-%02x-%02x-%02x-%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + + return ADDR_BROADCAST; } - logerror("="); - for ( i = 0 ; i < ETHERNET_ADDR_SIZE ; i++ ) + + // multicast + /* + * Multicast address matching is performed by computing the fcs crc of + * the destination address, and then using the upper 6 bits as an index + * into the 64-bit logical address filter. + */ + // Check for all multicast bit + if (m_reg[B0_RCR] & ALMUL) + return ADDR_MULTICAST; + + u32 const crc = util::crc32_creator::simple(buf, 6); + // The hash is based on the top 6 MSBs of the CRC + // The CRC needs to be inverted and reflected + m_rx_hash = 0x0; + for (int i = 0; i < 6; i++) + m_rx_hash |= (((~crc) >> i) & 1) << (5 - i); + u64 multicast_addr = *(u64*)&m_reg[B3_MT0_1]; + if (BIT(multicast_addr, m_rx_hash)) { - logerror("%.2X",mac_address[i]); + LOGMASKED(LOG_FILTER, "address_filter accepted (multicast address match) %02x-%02x-%02x-%02x-%02x-%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + + return ADDR_MULTICAST; } - logerror("?"); + LOGMASKED(LOG_FILTER, "address_filter rejected multicast %02x-%02x-%02x-%02x-%02x-%02x crc: %08x hash: %02x multi: %16ullx\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], crc, m_rx_hash, *(u64*)&m_reg[B3_MT0_1]); } - - // skip Ethernet broadcast packets if RECV_BROAD is not set - if (is_broadcast(mac_address)) + else { - LOG(" -- Broadcast rx\n"); - return 2; - } + // unicast + if (!memcmp(&m_reg[B1_IA0_1], buf, 6)) + { + LOGMASKED(LOG_FILTER, "address_filter accepted (physical address match)\n"); - if (memcmp(mac_address, &m_reg[EREG_IA0_1], ETHERNET_ADDR_SIZE) == 0) - { - LOG(" -- Address Match\n"); - return 1; + return ADDR_UNICAST; + } } - - LOG(" -- Not Matching\n"); - - return 0; + return ADDR_NOMATCH; } -/*************************************************************************** - recv_cb - receive callback - receive and process an ethernet packet - ***************************************************************************/ +/*------------------------------------------------- +recv_start_cb - Start receiving packet + A return value of 0 will stop rx processing in dinetwork device + Any other value will be sent to the recv_complete_cb +-------------------------------------------------*/ -void smc91c9x_device::recv_cb(uint8_t *data, int length) +int smc91c9x_device::recv_start_cb(u8 *buf, int length) { - LOG("recv_cb : %d/0x%x\n",length,length); + // check internal loopback + if (m_reg[B0_TCR] & (EPH_LOOP | LOOP)) + { + LOGMASKED(LOG_RX, "receive internal loopback mode, external packet discarded\n"); - int const isforme = ethernet_packet_is_for_me( data ); + return 0; + } - if (isforme==1 && (length >= ETHERNET_ADDR_SIZE) && (VERBOSE & LOG_GENERAL)) + // discard bad length packets + if (length < 64 || length > 256*6 - 6) { - logerror("RX: "); - for (int i = 0; i < ETHERNET_ADDR_SIZE; i++) - logerror("%.2X", data[i]); + LOGMASKED(LOG_RX, "received bad length packet length %d discarded\n", length); - logerror(" "); + return 0; + } - for (int i = 0; i < length-ETHERNET_ADDR_SIZE; i++) - logerror("%.2X", data[ETHERNET_ADDR_SIZE + i]); + // discard packets not from WMS + if (memcmp(WMS_OUI, &buf[6], 3)) + { + LOGMASKED(LOG_RX, "received non-WMS packet OUI: %02x:%02x:%02x length %d discarded\n", buf[6], buf[7], buf[8], length); - logerror(" - IsForMe %d - %d/0x%x bytes\n", isforme, length, length); + return 0; } - if ( (length < ETHERNET_ADDR_SIZE || !isforme) && !(m_reg[EREG_RCR] & 0x0102) ) + // Check for active transmission + if (m_tx_active) { - LOG("\n"); + // TODO: Update collision counters + LOGMASKED(LOG_RX, "transmit active COLLISION, rx packet length %d discarded\n", length); + + return 0; - // skip packet - return; } - /* signal a receive */ + return receive(buf, length); +} + +/*------------------------------------------------- +receive - Receive data into buffer + Returns the buffer packet number + 1 if successful +-------------------------------------------------*/ + +int smc91c9x_device::receive(u8 *buf, int length) +{ + // check receiver enabled + if (!(m_reg[B0_RCR] & RXEN)) + { + LOGMASKED(LOG_RX, "receive disabled, external packet discarded\n"); + + return -1; + } + + // address filter + int filter = address_filter(buf); + if (filter == ADDR_NOMATCH) + return -1; + + LOGMASKED(LOG_RX, "receive packet length %d\n", length); + dump_bytes(buf, length); // Try to request a packet number int packet_num; - if (!alloc_req(0, packet_num)) { - logerror("recv_cb: Couldn't allocate a receive packet\n"); - return; + if (!alloc_req(0, packet_num)) + { + logerror("recv_cb: Couldn't allocate memory for receive packet\n"); + return -2; } - /* compute the packet length */ + m_rx_active = 1; - if ( ( length < ( ETHER_BUFFER_SIZE - ( 2+2+2 ) ) ) ) - { - uint8_t *const packet = &m_buffer[ packet_num * ETHER_BUFFER_SIZE]; + // build up the packet + uint8_t *const packet = &m_buffer[packet_num * ETHER_BUFFER_SIZE]; - int dst = 0; + // Strip CRC + if (m_reg[B0_RCR] & STRIP_CRC) + length -= 4; - // build up the packet + // Copy received payload + memcpy(&packet[4], buf, length); - // Status word - packet[dst++] = 0x00; + // Status word + u16 *rx_status = (u16*)&packet[0]; + *rx_status = 0x0000; - // set the broadcast flag - if ( isforme == 2 ) - packet[dst++] |= 0x40; - else - packet[dst++] = 0x00; + // set the broadcast flag + if (filter == ADDR_BROADCAST) + *rx_status |= BRODCAST; - //bytes count - packet[dst++] = 0x00; - packet[dst++] = 0x00; + // set the multicast flag and hash + if (filter == ADDR_MULTICAST) + { + *rx_status |= (m_rx_hash << 1) | MULTCAST; + } - memcpy(&packet[dst], data, length ); - dst += length; + // Calculate buffer length and set control byte + u16 buf_length; - if ( dst & 1 ) - { - // ODD Frame - packet[dst++] = 0x40 | 0x20; // Control - } - else - { - packet[dst++] = 0x00; // Pad - packet[dst++] = 0x40 | 0x00; // Control - } + if (length & 1) + { + // ODD Frame + *rx_status |= ODDFRM; + packet[length + 4] = EBUF_RX_ALWAYS | EBUF_ODD; // Control + buf_length = length + 5; + } + else + { + packet[length + 4] = 0x00; // Pad + packet[length + 5] = EBUF_RX_ALWAYS; // Control + buf_length = length + 6; + } - //dst += 2; + // Set buffer length word + *(u16*)&packet[2] = buf_length; - dst &= 0x7FF; + return packet_num + 1; +} - packet[2] = (dst&0xFF); - packet[3] = (dst) >> 8; +/*------------------------------------------------- +recv_complete_cb - End of receive +-------------------------------------------------*/ +void smc91c9x_device::recv_complete_cb(int result) +{ + if (result > 0) + { // Push packet number to rx completion fifo - m_comp_rx.push_back(packet_num); + push_completed_rx(result - 1); } - else + // Couldn't allocate memory + else if (result == -2) { - LOG("Rejected ! Fifo Full ?"); + m_reg[B2_INTERRUPT] |= EINT_ALLOC; } update_ethernet_irq(); - LOG("\n"); + m_rx_active = 0; } -/*************************************************************************** - INTERNAL HELPERS -***************************************************************************/ - /*------------------------------------------------- - update_ethernet_irq - update the IRQ state + tx_poll - Starts transmit -------------------------------------------------*/ -void smc91c9x_device::update_ethernet_irq() +TIMER_CALLBACK_MEMBER(smc91c9x_device::tx_poll) { - // Check tx completion fifo empty - if (m_comp_tx.empty()) - m_reg[EREG_INTERRUPT] &= ~EINT_TX; - else - m_reg[EREG_INTERRUPT] |= EINT_TX; - - // Check rx completion fifo empty - if (m_comp_rx.empty()) - m_reg[EREG_INTERRUPT] &= ~EINT_RCV; - else - m_reg[EREG_INTERRUPT] |= EINT_RCV; + // Check for active RX and delay if necessary + if (m_rx_active) + { + // TODO: Implement correct CSMA/CD algorithm + m_tx_poll->adjust(attotime::from_usec(40)); + m_tx_retry_count++; + LOGMASKED(LOG_TX, "tx_poll: Delaying TX due to active RX retry_count = %d\n", m_tx_retry_count); + } + // Check if TX is enabled and packet is queued + else if ((m_reg[B0_TCR] & TXENA) && !empty_queued_tx()) + { + // Reset retry count + m_tx_retry_count = 0; - uint8_t const mask = m_reg[EREG_INTERRUPT] >> 8; - uint8_t const state = m_reg[EREG_INTERRUPT] & 0xff; + // Get the packet number from the transmit fifo + const int packet_num = curr_queued_tx(); + uint8_t *const tx_buffer = &m_buffer[packet_num * ETHER_BUFFER_SIZE]; + // Get the length and control fields from buffer + u16 length = (*(u16*)&tx_buffer[2]) & 0x7ff; + const u8 control = tx_buffer[length - 1]; - /* update the IRQ state */ - uint8_t new_state = mask & state; - if (m_irq_state ^ new_state) - { - LOG("update_ethernet_irq: old: %02x new: %02x\n", m_irq_state, new_state); - m_irq_state = new_state; - m_irq_handler(m_irq_state ? ASSERT_LINE : CLEAR_LINE); - } -} + // Remove [pad], control + if (control & EBUF_ODD) + length -= 1; + else + length -= 2; + // Add padding up to CRC area + // take into account status & length removal (-4) and crc addtion (+4) + while (length < 64 + 4 - 4 && (m_reg[B0_TCR] & PAD_EN)) + tx_buffer[length++] = 0x00; -/*------------------------------------------------- - update_stats - draw statistics --------------------------------------------------*/ + // Add CRC + // TODO: Calculate CRC + if (1 && ((control & EBUF_CRC) || !(m_reg[B0_TCR] & NOCRC))) + { + tx_buffer[length++] = 0x11; + tx_buffer[length++] = 0x22; + tx_buffer[length++] = 0x33; + tx_buffer[length++] = 0x44; + } -void smc91c9x_device::update_stats() -{ - if ( DISPLAY_STATS ) - popmessage("Sent:%d Rec'd:%d", m_sent, m_recd); -} + // Remove status, length + length -= 4; + // Reset the EPH register */ + m_reg[B0_EPH_STATUS] &= LINK_OK; -/*------------------------------------------------- - send_frame - push a frame to the interface --------------------------------------------------*/ + // Send the frame + m_tx_active = 1; + m_tx_poll->enable(false); -TIMER_CALLBACK_MEMBER(smc91c9x_device::send_frame) -{ - // Get the packet number from the transmit fifo - const int packet_num = m_trans_tx.front(); - uint8_t *const tx_buffer = &m_buffer[packet_num * ETHER_BUFFER_SIZE]; + LOGMASKED(LOG_TX, "Start sending packet %d length = %d time: %s\n", packet_num, length, machine().scheduler().time().as_string()); + dump_bytes(&tx_buffer[4], length); - // Pop the transmit fifo - m_trans_tx.erase(m_trans_tx.begin()); + // Write loopback data and save result + if (m_reg[B0_TCR] & (EPH_LOOP | LOOP | FDUPLX)) + m_loopback_result = receive(&tx_buffer[4], length); + else + m_loopback_result = 0; - /* update the EPH register */ - m_reg[EREG_EPH_STATUS] = 0x0001; + // Local loopback isn't sent to cable + //if ((m_reg[B0_TCR] & (EPH_LOOP | LOOP) || (get_interface() < 0 && (m_reg[B0_TCR] & FDUPLX)))) + if (m_reg[B0_TCR] & (EPH_LOOP | LOOP)) + send_complete_cb(length); + else + send(&tx_buffer[4], length); - if (is_broadcast(&tx_buffer[4])) - m_reg[EREG_EPH_STATUS] |= 0x0040; + } +} - // Check tx completion fifo empty - if (m_trans_tx.empty()) - m_reg[EREG_INTERRUPT] |= EINT_TX_EMPTY; +/*------------------------------------------------- +send_complete_cb - Called after transmit complete +-------------------------------------------------*/ +void smc91c9x_device::send_complete_cb(int result) +{ m_sent++; - update_stats(); - int buffer_len = ((tx_buffer[3] << 8) | tx_buffer[2]) & 0x7ff; - // Remove status, length, [pad], control - if (tx_buffer[buffer_len - 1] & 0x20) - buffer_len -= 5; - else - buffer_len -= 6; - // Add padding - if (buffer_len < 64 && (m_reg[EREG_TCR] & 0x0080)) { - while (buffer_len < 64) - tx_buffer[4 + buffer_len++] = 0x00; - } - if (VERBOSE & LOG_GENERAL) - { - logerror("TX: "); - for (int i = 0; i < ETHERNET_ADDR_SIZE; i++) - logerror("%.2X", tx_buffer[4 + i]); + // Pop the packet number from the transmit fifo + const int packet_num = pop_queued_tx(); + uint8_t *const tx_buffer = &m_buffer[packet_num * ETHER_BUFFER_SIZE]; - logerror(" "); + LOGMASKED(LOG_TX, "End sending packet %d result = %d time: %s\n", packet_num, result, machine().scheduler().time().as_string()); - for (int i = ETHERNET_ADDR_SIZE; i < buffer_len; i++) - logerror("%.2X", tx_buffer[4 + i]); + /* update the EPH register */ + m_reg[B0_EPH_STATUS] |= TX_SUC; - logerror("--- %d/0x%x bytes\n", buffer_len, buffer_len); + // Set LINK_OK in status + if (0 && !(m_reg[B0_EPH_STATUS] & LINK_OK)) + { + m_reg[B0_EPH_STATUS] |= LINK_OK; + // Set a ethernet phy status interrupt + m_reg[B2_INTERRUPT] |= EINT_EPH; } - if (buffer_len > 4) - { - if (m_link_unconnected) - { - // Set lost carrier - if (m_reg[EREG_TCR] & 0x0400) - { - m_reg[EREG_EPH_STATUS] |= 0x400; - // Clear Tx Enable on error - m_reg[EREG_TCR] &= ~0x1; - } + // Set Tx broadcast flag + if (!memcmp(ETH_BROADCAST, &tx_buffer[4], 6)) + m_reg[B0_EPH_STATUS] |= LTX_BRD; - // Set signal quality error - if (m_reg[EREG_TCR] & 0x1000) - { - m_reg[EREG_EPH_STATUS] |= 0x20; - // Clear Tx Enable on error - m_reg[EREG_TCR] &= ~0x1; - } + // Check tx queued fifo empty + if (empty_queued_tx()) + m_reg[B2_INTERRUPT] |= EINT_TX_EMPTY; - // Set a ethernet phy status interrupt - m_reg[EREG_INTERRUPT] |= EINT_EPH; + // Set no-transmission flags + if (m_link_unconnected) + { + //m_reg[B0_EPH_STATUS] &= ~LINK_OK; + //m_reg[B0_EPH_STATUS] &= ~TX_SUC; - // TODO: Is it necessary to clear FIFOs on error? - // Flush fifos. - //clear_tx_fifo(); - //clear_rx_fifo(); - } - else + // Set lost carrier + if (m_reg[B0_TCR] & MON_CSN) { - // Send the frame - if (!send(&tx_buffer[4], buffer_len)) - { - // FIXME: failed to send the Ethernet packet - //logerror("failed to send Ethernet packet\n"); - //LOG(this,("read_command_port(): !!! failed to send Ethernet packet")); - } + m_reg[B0_EPH_STATUS] |= LOST_CARR; + // Clear Tx Enable on error + m_reg[B0_TCR] &= ~TXENA; + } - // Loopback if loopback is set or fduplx is set - // TODO: Figure out correct size - // TODO: Check for addtional filter options for FDUPLX mode - if ((m_reg[EREG_TCR] & 0x2002) || (m_network_available && (m_reg[EREG_TCR] & 0x0800))) - recv_cb(&tx_buffer[4], buffer_len); + // Set signal quality error + if (m_reg[B0_TCR] & STP_SQET) + { + m_reg[B0_EPH_STATUS] |= SQET; + // Clear Tx Enable on error + m_reg[B0_TCR] &= ~TXENA; } + + // Set a ethernet phy status interrupt + m_reg[B2_INTERRUPT] |= EINT_EPH; } + // Update status in the transmit word - tx_buffer[0] = m_reg[EREG_EPH_STATUS]; - tx_buffer[1] = m_reg[EREG_EPH_STATUS] >> 8; + *(u16*)&tx_buffer[0] = m_reg[B0_EPH_STATUS]; // Push the packet number onto the tx completion fifo - m_comp_tx.push_back(packet_num); + push_completed_tx(packet_num); update_ethernet_irq(); - // If there is more packets to transmit then set the tx timer - if ((m_reg[EREG_TCR] & 0x1) && !m_trans_tx.empty()) { - // Shortest packet (64 bytes @ 10Mbps = 50us) - m_tx_timer->adjust(attotime::from_usec(50)); + // Loopback if loopback is set or fduplx is set + if (m_loopback_result) + { + //int rx_result = receive(&tx_buffer[4], result); + recv_complete_cb(m_loopback_result); } + + // If there is more packets to transmit then start the tx polling + if ((m_reg[B0_TCR] & TXENA) && !empty_queued_tx()) + { + m_tx_poll->adjust(attotime::from_usec(10)); + } + + m_tx_active = 0; } /*------------------------------------------------- @@ -662,53 +695,48 @@ void smc91c9x_device::process_command(uint16_t data) LOG(" ALLOCATE MEMORY FOR TX (%d)", (data & 7)); { int packet_num; - if (alloc_req(1, packet_num)) { + if (alloc_req(1, packet_num)) + { LOG(" packet_num = %02x\n", (packet_num)); // Set ARR register - m_reg[EREG_PNR_ARR] &= ~0xff00; - m_reg[EREG_PNR_ARR] |= packet_num << 8; - m_reg[EREG_INTERRUPT] |= EINT_ALLOC; + m_reg[B2_PNR_ARR] &= ~0xff00; + m_reg[B2_PNR_ARR] |= packet_num << 8; + m_reg[B2_INTERRUPT] |= EINT_ALLOC; update_ethernet_irq(); } - else { + else + { logerror("ECMD_ALLOCATE: Couldn't allocate TX memory\n"); } } break; case ECMD_RESET_MMU: - /* - 0100 - - RESET MMU TO INITIAL STATE - - Frees all memory allocations, clears relevant - interrupts, resets packet FIFO pointers. - */ - LOG(" RESET MMU\n"); mmu_reset(); break; case ECMD_REMOVE_TOPFRAME_TX: LOG(" REMOVE FRAME FROM TX FIFO\n"); - if (m_comp_tx.empty()) + if (empty_completed_tx()) logerror("process_command: Trying to remove entry from empty tx completion fifo\n"); else - m_comp_tx.erase(m_comp_tx.begin()); + pop_completed_tx(); break; case ECMD_REMOVE_RELEASE_TOPFRAME_RX: - LOG(" REMOVE AND RELEASE FRAME FROM RX FIFO (PACK_NUM=%d)\n", m_comp_rx.front()); + LOG(" REMOVE AND RELEASE FRAME FROM RX FIFO (PACK_NUM=%d)\n", curr_completed_rx()); // Release memory allocation - alloc_release(m_comp_rx.front()); + alloc_release(curr_completed_rx()); // Fall through case ECMD_REMOVE_TOPFRAME_RX: LOG(" REMOVE FRAME FROM RX FIFO\n"); // remove entry from rx completion queue - if (m_comp_rx.empty()) + if (empty_completed_rx()) logerror("process_command: Trying to remove entry from empty rx completion fifo\n"); else - m_comp_rx.erase(m_comp_rx.begin()); + pop_completed_rx(); update_ethernet_irq(); m_recd++; @@ -717,7 +745,7 @@ void smc91c9x_device::process_command(uint16_t data) case ECMD_RELEASE_PACKET: { - const int packet_number = m_reg[EREG_PNR_ARR] & 0xff; + const int packet_number = m_reg[B2_PNR_ARR] & 0xff; alloc_release(packet_number); LOG(" RELEASE SPECIFIC PACKET %d\n", packet_number); } @@ -726,33 +754,31 @@ void smc91c9x_device::process_command(uint16_t data) case ECMD_ENQUEUE_PACKET: LOG(" ENQUEUE TX PACKET "); - if (m_reg[EREG_TCR] & 0x0001) // TX EN ? + if (m_reg[B0_TCR] & TXENA) { - const int packet_number = m_reg[EREG_PNR_ARR] & 0xff; + const int packet_number = m_reg[B2_PNR_ARR] & 0xff; LOG("(PACKET_NUM=%d)\n", packet_number); // Push packet number to tx transmit fifo - m_trans_tx.push_back(packet_number); - // Calculate transmit time - //uint8_t *const tx_buffer = &m_buffer[packet_number * ETHER_BUFFER_SIZE]; - //int buffer_len = ((tx_buffer[3] << 8) | tx_buffer[2]) & 0x7ff; - //buffer_len -= 6; - //int usec = ((buffer_len * 8) / 10) + 1; - // Shortest packet (64 bytes @ 10Mbps = 50us) - m_tx_timer->adjust(attotime::from_usec(50)); + push_queued_tx(packet_number); + // Start timer to send frame if not already transmitting + if (!m_tx_active && !m_tx_poll->enabled()) + { + m_tx_poll->adjust(attotime::from_usec(10)); + LOG("Start polling time: %s\n", machine().scheduler().time().as_string()); + } } break; case ECMD_RESET_FIFOS: LOG(" RESET TX FIFOS\n"); // Flush fifos. - clear_tx_fifo(); + reset_tx_fifos(); break; } // Set Busy (clear on next read) - m_reg[EREG_MMU_COMMAND] |= 0x0001; - //LOG("process_command: TxQ: %d TxComp: %d RxComp: %d TxAlloc: %04x RxAlloc: %04x\n", - // m_trans_tx.size(), m_comp_tx.size(), m_comp_rx.size(), m_alloc_tx, m_alloc_rx); + m_reg[B2_MMU_COMMAND] |= 0x0001; + } @@ -771,60 +797,60 @@ READ16_MEMBER( smc91c9x_device::read ) /* determine the effective register */ offset %= 8; - if ( offset != EREG_BANK ) - offset += 8 * (m_reg[EREG_BANK] & 7); + if ( offset != B0_BANK ) + offset += 8 * (m_reg[B0_BANK] & 7); result = m_reg[offset]; switch (offset) { - case EREG_MMU_COMMAND: + case B2_MMU_COMMAND: // Clear busy - m_reg[EREG_MMU_COMMAND] &= ~0x0001; + m_reg[B2_MMU_COMMAND] &= ~0x0001; break; - case EREG_PNR_ARR: + case B2_PNR_ARR: if ( ACCESSING_BITS_8_15 ) { - m_reg[EREG_INTERRUPT] &= ~EINT_ALLOC; + m_reg[B2_INTERRUPT] &= ~EINT_ALLOC; update_ethernet_irq(); } break; - case EREG_FIFO_PORTS: + case B2_FIFO_PORTS: result = 0; - if (!m_comp_tx.empty()) - result |= m_comp_tx.front(); + if (!empty_completed_tx()) + result |= curr_completed_tx(); else result |= 0x80; - if (!m_comp_rx.empty()) - result |= m_comp_rx.front() << 8; + if (!empty_completed_rx()) + result |= curr_completed_rx() << 8; else result |= 0x80 << 8; break; - case EREG_DATA_0: /* data register */ - case EREG_DATA_1: /* data register */ + case B2_DATA_0: /* data register */ + case B2_DATA_1: /* data register */ { uint8_t *buffer; - int addr = m_reg[EREG_POINTER] & 0x7ff; + int addr = m_reg[B2_POINTER] & 0x7ff; - if ( m_reg[EREG_POINTER] & 0x8000 ) - buffer = &m_buffer[m_comp_rx.front() * ETHER_BUFFER_SIZE]; + if ( m_reg[B2_POINTER] & 0x8000 ) + buffer = &m_buffer[curr_completed_rx() * ETHER_BUFFER_SIZE]; else - buffer = &m_buffer[(m_reg[EREG_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];; + buffer = &m_buffer[(m_reg[B2_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];; result = buffer[addr++]; if ( ACCESSING_BITS_8_15 ) result |= buffer[addr++] << 8; - if ( m_reg[EREG_POINTER] & 0x4000 ) - m_reg[EREG_POINTER] = (m_reg[EREG_POINTER] & ~0x7ff) | (addr & 0x7ff); + if ( m_reg[B2_POINTER] & 0x4000 ) + m_reg[B2_POINTER] = (m_reg[B2_POINTER] & ~0x7ff) | (addr & 0x7ff); break; } } - if (offset != EREG_BANK) + if (offset != B0_BANK) LOG("%s:smc91c9x_r(%s) = %04X & %04X\n", machine().describe_context(), ethernet_regname[offset], result, mem_mask); return result; } @@ -838,12 +864,12 @@ WRITE16_MEMBER( smc91c9x_device::write ) { /* determine the effective register */ offset %= 8; - if (offset != EREG_BANK) - offset += 8 * (m_reg[EREG_BANK] & 7); + if (offset != B0_BANK) + offset += 8 * (m_reg[B0_BANK] & 7); /* update the data generically */ - if (offset != EREG_BANK && offset < sizeof(m_reg)) + if (offset != B0_BANK && offset < sizeof(m_reg)) LOG("%s:smc91c9x_w(%s) = [%04X]<-%04X & (%04X & %04X)\n", machine().describe_context(), ethernet_regname[offset], offset, data, mem_mask , m_regmask[offset]); mem_mask &= m_regmask[offset]; @@ -852,49 +878,62 @@ WRITE16_MEMBER( smc91c9x_device::write ) /* handle it */ switch (offset) { - case EREG_TCR: /* transmit control register */ + case B0_TCR: /* transmit control register */ // Setting Tx Enable clears some status and interrupts - if ( data & 0x1 ) { - m_reg[EREG_EPH_STATUS] &= ~0x420; - m_reg[EREG_INTERRUPT] &= ~EINT_EPH; - update_ethernet_irq(); + if ( data & TXENA ) + { + if (m_reg[B0_EPH_STATUS] & (LOST_CARR | SQET | LATCOL | E16COL)) + { + m_reg[B0_EPH_STATUS] &= ~(LOST_CARR | SQET | LATCOL | E16COL); + m_reg[B2_INTERRUPT] &= ~EINT_EPH; + update_ethernet_irq(); + } + } + if (VERBOSE & LOG_GENERAL) + { + if (data & FDSE) LOG(" FDSE\n"); + if (data & EPH_LOOP) LOG(" EPH LOOP\n"); + if (data & STP_SQET) LOG(" STP SQET\n"); + if (data & FDUPLX) LOG(" FDUPLX\n"); + if (data & MON_CSN) LOG(" MON_CSN\n"); + if (data & NOCRC) LOG(" NOCRC\n"); + if (data & PAD_EN) LOG(" PAD_EN\n"); + if (data & FORCOL) LOG(" FORCOL\n"); + if (data & LOOP) LOG(" LOOP\n"); + if (data & TXENA) LOG(" TXENA\n"); } - - if (data & 0x2000) LOG(" EPH LOOP\n"); - if (data & 0x1000) LOG(" STP SQET\n"); - if (data & 0x0800) LOG(" FDUPLX\n"); - if (data & 0x0400) LOG(" MON_CSN\n"); - if (data & 0x0100) LOG(" NOCRC\n"); - if (data & 0x0080) LOG(" PAD_EN\n"); - if (data & 0x0004) LOG(" FORCOL\n"); - if (data & 0x0002) LOG(" LOOP\n"); - if (data & 0x0001) LOG(" TXENA\n"); break; - case EREG_RCR: /* receive control register */ + case B0_RCR: /* receive control register */ - if ( data & 0x8000 ) + if ( data & SOFT_RST) { - clear_rx_fifo(); - clear_tx_fifo(); + reset(); } - if ( !(data & 0x0100) ) + if ( !(data & RXEN) ) + { + reset_completed_rx(); + } + if (data & RXEN) { - clear_rx_fifo(); + // Set LINK_OK in status + m_reg[B0_EPH_STATUS] |= LINK_OK; } - if (data & 0x8000) reset(); - if (data & 0x8000) LOG(" SOFT RST\n"); - if (data & 0x4000) LOG(" FILT_CAR\n"); - if (data & 0x0200) LOG(" STRIP CRC\n"); - if (data & 0x0100) LOG(" RXEN\n"); - if (data & 0x0004) LOG(" ALMUL\n"); - if (data & 0x0002) LOG(" PRMS\n"); - if (data & 0x0001) LOG(" RX_ABORT\n"); + if (VERBOSE & LOG_GENERAL) + { + if (data & SOFT_RST) LOG(" SOFT RST\n"); + if (data & FILT_CAR) LOG(" FILT_CAR\n"); + if (data & STRIP_CRC) LOG(" STRIP CRC\n"); + if (data & RXEN) LOG(" RXEN\n"); + if (data & ALMUL) LOG(" ALMUL\n"); + if (data & PRMS) LOG(" PRMS\n"); + if (data & RX_ABORT) LOG(" RX_ABORT\n"); + } break; - case EREG_CONFIG: /* configuration register */ + case B1_CONFIG: /* configuration register */ if (data & 0x1000) LOG(" NO WAIT\n"); if (data & 0x0400) LOG(" FULL STEP\n"); if (data & 0x0200) LOG(" SET SQLCH\n"); @@ -905,64 +944,86 @@ WRITE16_MEMBER( smc91c9x_device::write ) if (data & 0x0002) LOG(" INT SEL0\n"); break; - case EREG_BASE: /* base address register */ + case B1_BASE: /* base address register */ LOG(" base = $%04X\n", (data & 0xe000) | ((data & 0x1f00) >> 3)); LOG(" romsize = %d\n", ((data & 0xc0) >> 6)); LOG(" romaddr = $%05X\n", ((data & 0x3e) << 13)); break; - case EREG_CONTROL: /* control register */ - if (data & 0x4000) LOG(" RCV_BAD\n"); - if (data & 0x2000) LOG(" PWRDN\n"); - if (data & 0x0800) LOG(" AUTO RELEASE\n"); - if (data & 0x0080) LOG(" LE ENABLE\n"); - if (data & 0x0040) LOG(" CR ENABLE\n"); - if (data & 0x0020) LOG(" TE ENABLE\n"); - if (data & 0x0004) LOG(" EEPROM SELECT\n"); - if (data & 0x0002) LOG(" RELOAD\n"); - if (data & 0x0001) LOG(" STORE\n"); + case B1_IA4_5: + set_promisc(m_reg[B0_RCR] & PRMS); + set_mac((char *)&m_reg[B1_IA0_1]); + + break; + + case B1_CONTROL: /* control register */ + // Clearing LE_EN clears interrupt from LINK_OK status change + if (!(data & LE_ENABLE)) + { + m_reg[B2_INTERRUPT] &= ~EINT_EPH; + update_ethernet_irq(); + } + if (0 && (data & LE_ENABLE)) + { + if (m_reg[B0_EPH_STATUS] & LINK_OK) + { + m_reg[B0_EPH_STATUS] &= ~(LINK_OK); + m_reg[B2_INTERRUPT] &= ~EINT_EPH; + update_ethernet_irq(); + } + } + if (VERBOSE & LOG_GENERAL) + { + if (data & RCV_BAD) LOG(" RCV_BAD\n"); + if (data & PWRDN) LOG(" PWRDN\n"); + if (data & WAKEUP_EN) LOG(" WAKEUP ENABLE\n"); + if (data & AUTO_RELEASE) LOG(" AUTO RELEASE\n"); + if (data & LE_ENABLE) LOG(" LE ENABLE\n"); + if (data & CR_ENABLE) LOG(" CR ENABLE\n"); + if (data & TE_ENABLE) LOG(" TE ENABLE\n"); + if (data & EEPROM_SEL) LOG(" EEPROM SELECT\n"); + if (data & RELOAD) LOG(" RELOAD\n"); + if (data & STORE) LOG(" STORE\n"); + } break; - case EREG_MMU_COMMAND: /* command register */ + case B2_MMU_COMMAND: /* command register */ process_command(data); break; - case EREG_DATA_0: /* data register */ - case EREG_DATA_1: /* data register */ + case B2_DATA_0: /* data register */ + case B2_DATA_1: /* data register */ { uint8_t *buffer; - int addr = m_reg[EREG_POINTER] & 0x7ff; + int addr = m_reg[B2_POINTER] & PTR; - if (m_reg[EREG_POINTER] & 0x8000) - buffer = &m_buffer[m_comp_rx.front() * ETHER_BUFFER_SIZE]; + if (m_reg[B2_POINTER] & RCV) + buffer = &m_buffer[curr_completed_rx() * ETHER_BUFFER_SIZE]; else - buffer = &m_buffer[(m_reg[EREG_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];; + buffer = &m_buffer[(m_reg[B2_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];; - // TODO: Should be checking if incr is set buffer[addr++] = data; if ( ACCESSING_BITS_8_15 ) buffer[addr++] = data >> 8; - if ( m_reg[EREG_POINTER] & 0x4000 ) - m_reg[EREG_POINTER] = (m_reg[EREG_POINTER] & ~0x7ff) | (addr & 0x7ff); + if ( m_reg[B2_POINTER] & AUTO_INCR) + m_reg[B2_POINTER] = (m_reg[B2_POINTER] & ~PTR) | (addr & PTR); break; } - case EREG_INTERRUPT: + case B2_INTERRUPT: // Pop tx fifo packet from completion fifo if clear tx int is set - if (m_reg[EREG_INTERRUPT] & data & EINT_TX) { - if (m_comp_tx.empty()) { + if (m_reg[B2_INTERRUPT] & data & EINT_TX) + { + if (empty_completed_tx()) logerror("write: Trying to remove an entry from empty tx completion fifo\n"); + else + { + LOG("Removing tx completion packet_num = %d\n", curr_completed_tx()); + pop_completed_tx(); } - else { - LOG("Removing tx completion packet_num = %d\n", m_comp_tx.front()); - m_comp_tx.erase(m_comp_tx.begin()); - } - } - // Clear TX_EMPTY interrupt if clear tx empty bit is set - if (m_reg[EREG_INTERRUPT] & data & EINT_TX_EMPTY) { - m_reg[EREG_INTERRUPT] &= ~EINT_TX_EMPTY; } - m_reg[EREG_INTERRUPT] &= ~(data & 0x56); + // Clear interrupts + m_reg[B2_INTERRUPT] &= ~(data & (EINT_ERCV | EINT_RX_OVRN | EINT_TX_EMPTY | EINT_TX)); update_ethernet_irq(); break; } |