diff options
Diffstat (limited to 'src/devices/machine/smc91c9x.cpp')
-rw-r--r-- | src/devices/machine/smc91c9x.cpp | 630 |
1 files changed, 443 insertions, 187 deletions
diff --git a/src/devices/machine/smc91c9x.cpp b/src/devices/machine/smc91c9x.cpp index ee5214d2323..b94f7a29d1f 100644 --- a/src/devices/machine/smc91c9x.cpp +++ b/src/devices/machine/smc91c9x.cpp @@ -4,12 +4,12 @@ SMC91C9X ethernet controller implementation - by Aaron Giles + by Aaron Giles, Jean-François DEL NERO *************************************************************************** Notes: - * only loopback mode really works + * Connected mode working **************************************************************************/ @@ -22,7 +22,9 @@ DEBUGGING ***************************************************************************/ -#define LOG_ETHERNET (0) +//#define VERBOSE 1 +#include "logmacro.h" + #define DISPLAY_STATS (0) @@ -68,14 +70,15 @@ #define EREG_ERCV (3*8 + 6) /* Ethernet MMU commands */ -#define ECMD_NOP 0 -#define ECMD_ALLOCATE 1 -#define ECMD_RESET_MMU 2 -#define ECMD_REMOVE 3 -#define ECMD_REMOVE_RELEASE 4 -#define ECMD_RELEASE_PACKET 5 -#define ECMD_ENQUEUE_PACKET 6 -#define ECMD_RESET_FIFOS 7 +#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 @@ -99,16 +102,15 @@ static const char *const ethernet_regname[64] = "(7.0)", "(7.1)", "(7.2)", "(7.3)", "(7.4)", "(7.5)", "(7.6)", "BANK" }; - - /*************************************************************************** DEVICE INTERFACE ***************************************************************************/ smc91c9x_device::smc91c9x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, type, tag, owner, clock) + , device_network_interface(mconfig, *this, 10.0f) , m_irq_handler(*this) - , m_link_unconnected(true) + , m_link_unconnected(false) { } @@ -119,15 +121,12 @@ smc91c9x_device::smc91c9x_device(const machine_config &mconfig, device_type type void smc91c9x_device::device_start() { m_irq_handler.resolve_safe(); - // TX timer - m_tx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(smc91c9x_device::finish_enqueue), this)); /* register ide states */ save_item(NAME(m_reg)); save_item(NAME(m_regmask)); save_item(NAME(m_irq_state)); save_item(NAME(m_alloc_count)); - save_item(NAME(m_fifo_count)); save_item(NAME(m_rx)); save_item(NAME(m_tx)); save_item(NAME(m_sent)); @@ -140,14 +139,39 @@ void smc91c9x_device::device_start() void smc91c9x_device::device_reset() { - memset(m_reg, 0, sizeof(m_reg)); - memset(m_regmask, 0, sizeof(m_regmask)); + std::fill(std::begin(m_reg), std::end(m_reg), 0); + + std::fill(std::begin(m_rx), std::end(m_rx), 0); + std::fill(std::begin(m_tx), std::end(m_tx), 0); + + std::fill(std::begin(m_regmask), std::end(m_regmask), 0); + m_irq_state = 0; m_alloc_count = 0; - m_fifo_count = 0; + rx_fifo_out = 0; + rx_fifo_in = 0; + + tx_fifo_out = 0; + tx_fifo_in = 0; + m_sent = 0; m_recd = 0; + 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); + 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; @@ -158,9 +182,17 @@ void smc91c9x_device::device_reset() m_reg[EREG_CONFIG] = 0x0030; m_regmask[EREG_CONFIG] = 0x17c6; m_reg[EREG_BASE] = 0x1866; m_regmask[EREG_BASE] = 0xfffe; - m_reg[EREG_IA0_1] = 0x0000; m_regmask[EREG_IA0_1] = 0xffff; - m_reg[EREG_IA2_3] = 0x0000; m_regmask[EREG_IA2_3] = 0xffff; - m_reg[EREG_IA4_5] = 0x0000; m_regmask[EREG_IA4_5] = 0xffff; + + // 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; + + // 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); + m_reg[EREG_GENERAL_PURP] = 0x0000; m_regmask[EREG_GENERAL_PURP] = 0xffff; m_reg[EREG_CONTROL] = 0x0100; m_regmask[EREG_CONTROL] = 0x68e7; @@ -181,7 +213,6 @@ void smc91c9x_device::device_reset() m_reg[EREG_ERCV] = 0x331f; m_regmask[EREG_ERCV] = 0x009f; update_ethernet_irq(); - m_tx_timer->adjust(attotime::never); } @@ -192,7 +223,6 @@ smc91c94_device::smc91c94_device(const machine_config &mconfig, const char *tag, { } - 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) @@ -200,6 +230,182 @@ smc91c96_device::smc91c96_device(const machine_config &mconfig, const char *tag, { } +void smc91c9x_device::clear_tx_fifo() +{ + tx_fifo_in = 0; + tx_fifo_out = 0; + std::fill(std::begin(m_tx), std::end(m_tx), 0); +} + +void smc91c9x_device::clear_rx_fifo() +{ + rx_fifo_in = 0; + rx_fifo_out = 0; + std::fill(std::begin(m_rx), std::end(m_rx), 0); +} + +int smc91c9x_device::is_broadcast(uint8_t mac_address[]) +{ + int i; + + i = 0; + + while(mac_address[i] == 0xFF) + { + i++; + } + + if ( i == 6 ) + return 1; + + return 0; +} + + +int smc91c9x_device::ethernet_packet_is_for_me(const uint8_t mac_address[]) +{ + // 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; + uint8_t local_address[ETHERNET_ADDR_SIZE]; + + LOG("\n"); + + local_address[0] = (m_reg[EREG_IA0_1]>>0) & 0xFF; + local_address[1] = (m_reg[EREG_IA0_1]>>8) & 0xFF; + local_address[2] = (m_reg[EREG_IA2_3]>>0) & 0xFF; + local_address[3] = (m_reg[EREG_IA2_3]>>8) & 0xFF; + local_address[4] = (m_reg[EREG_IA4_5]>>0) & 0xFF; + local_address[5] = (m_reg[EREG_IA4_5]>>8) & 0xFF; + + if (VERBOSE & LOG_GENERAL) + { + for ( i = 0 ; i < ETHERNET_ADDR_SIZE ; i++ ) + { + logerror("%.2X",local_address[i]); + } + logerror("="); + for ( i = 0 ; i < ETHERNET_ADDR_SIZE ; i++ ) + { + logerror("%.2X",mac_address[i]); + } + logerror("?"); + } + + // skip Ethernet broadcast packets if RECV_BROAD is not set + if (is_broadcast((uint8_t *)mac_address)) + { + LOG(" -- Broadcast rx\n"); + return 2; + } + + if (memcmp(mac_address, local_address, ETHERNET_ADDR_SIZE) == 0) + { + LOG(" -- Address Match\n"); + return 1; + } + + LOG(" -- Not Matching\n"); + + return 0; +} + +/*************************************************************************** + recv_cb - receive callback - receive and process an ethernet packet + ***************************************************************************/ + +void smc91c9x_device::recv_cb(uint8_t *data, int length) +{ + LOG("recv_cb : %d/0x%x\n",length,length); + + int const isforme = ethernet_packet_is_for_me( data ); + + if (isforme==1 && (length >= ETHERNET_ADDR_SIZE) && (VERBOSE & LOG_GENERAL)) + { + logerror("RX: "); + for (int i = 0; i < ETHERNET_ADDR_SIZE; i++) + logerror("%.2X", data[i]); + + logerror(" "); + + for (int i = 0; i < length-ETHERNET_ADDR_SIZE; i++) + logerror("%.2X", data[ETHERNET_ADDR_SIZE + i]); + + logerror(" - IsForMe %d - %d/0x%x bytes\n", isforme, length, length); + } + + if ( (length < ETHERNET_ADDR_SIZE || !isforme) && !(m_reg[EREG_RCR] & 0x0100) ) + { + LOG("\n"); + + // skip packet + return; + } + + /* signal a receive */ + + /* compute the packet length */ + + if ( ( length < ( ETHER_BUFFER_SIZE - ( 2+2+2 ) ) ) ) + { + uint8_t *const packet = &m_rx[ ( rx_fifo_in & ( ETHER_RX_BUFFERS - 1 ) ) * ETHER_BUFFER_SIZE]; + + std::fill_n(packet, ETHER_BUFFER_SIZE, 0); + + int dst = 0; + + // build up the packet + + // Status word + packet[dst++] = 0x00; + + // set the broadcast flag + if ( isforme == 2 ) + packet[dst++] |= 0x40; + else + packet[dst++] = 0x00; + + //bytes count + packet[dst++] = 0x00; + packet[dst++] = 0x00; + + memcpy(&packet[dst], data, length ); + dst += length; + + if ( dst & 1 ) + { + // ODD Frame + packet[dst++] = 0x40 | 0x20; // Control + } + else + { + packet[dst++] = 0x00; // Pad + packet[dst++] = 0x40 | 0x00; // Control + } + + dst += 2; + + dst &= 0x7FF; + + packet[2] = (dst&0xFF); + packet[3] = (dst) >> 8; + + m_reg[EREG_INTERRUPT] |= EINT_RCV; + m_reg[EREG_FIFO_PORTS] &= ~0x8000; + + rx_fifo_in = (rx_fifo_in + 1) & ( ETHER_RX_BUFFERS - 1 ); + } + else + { + LOG("Rejected ! Fifo Full ?"); + } + + update_ethernet_irq(); + + LOG("\n"); +} + /*************************************************************************** INTERNAL HELPERS ***************************************************************************/ @@ -210,14 +416,12 @@ smc91c96_device::smc91c96_device(const machine_config &mconfig, const char *tag, void smc91c9x_device::update_ethernet_irq() { - uint8_t mask = m_reg[EREG_INTERRUPT] >> 8; - uint8_t state = m_reg[EREG_INTERRUPT] & 0xff; + uint8_t const mask = m_reg[EREG_INTERRUPT] >> 8; + uint8_t const state = m_reg[EREG_INTERRUPT] & 0xff; /* update the IRQ state */ m_irq_state = ((mask & state) != 0); - if (!m_irq_handler.isnull()) { - m_irq_handler(m_irq_state ? ASSERT_LINE : CLEAR_LINE); - } + m_irq_handler(m_irq_state ? ASSERT_LINE : CLEAR_LINE); } @@ -227,97 +431,84 @@ void smc91c9x_device::update_ethernet_irq() void smc91c9x_device::update_stats() { - if (DISPLAY_STATS) + if ( DISPLAY_STATS ) popmessage("Sent:%d Rec'd:%d", m_sent, m_recd); } /*------------------------------------------------- - finish_enqueue - complete an enqueued packet + send_frame - push a frame to the interface -------------------------------------------------*/ -TIMER_CALLBACK_MEMBER(smc91c9x_device::finish_enqueue) +int smc91c9x_device::send_frame() { - int is_broadcast = (m_tx[4] == 0xff && m_tx[5] == 0xff && m_tx[6] == 0xff && + bool const is_broadcast = (m_tx[4] == 0xff && m_tx[5] == 0xff && m_tx[6] == 0xff && m_tx[7] == 0xff && m_tx[8] == 0xff && m_tx[9] == 0xff); + tx_fifo_in = ( tx_fifo_in + 1 ) & ( ETHER_TX_BUFFERS - 1 ); + + uint8_t *const tx_buffer = &m_tx[(tx_fifo_out & (ETHER_TX_BUFFERS-1))* ETHER_BUFFER_SIZE]; + tx_fifo_out = ((tx_fifo_out + 1)& (ETHER_TX_BUFFERS-1)); + /* update the EPH register and stuff it in the first transmit word */ m_reg[EREG_EPH_STATUS] = 0x0001; + if (is_broadcast) m_reg[EREG_EPH_STATUS] |= 0x0040; - m_tx[0] = m_reg[EREG_EPH_STATUS]; - m_tx[1] = m_reg[EREG_EPH_STATUS] >> 8; + + tx_buffer[0] = m_reg[EREG_EPH_STATUS]; + tx_buffer[1] = m_reg[EREG_EPH_STATUS] >> 8; /* signal a transmit interrupt and mark the transmit buffer empty */ m_reg[EREG_INTERRUPT] |= EINT_TX; m_reg[EREG_INTERRUPT] |= EINT_TX_EMPTY; m_reg[EREG_FIFO_PORTS] |= 0x0080; m_sent++; + update_stats(); - /* loopback? */ - if (m_reg[EREG_TCR] & 0x2002) { - if (m_fifo_count < ETHER_RX_BUFFERS) - { - int buffer_len = ((m_tx[3] << 8) | m_tx[2]) & 0x7ff; - uint8_t *packet = &m_rx[m_fifo_count++ * ETHER_BUFFER_SIZE]; - int packet_len; - - /* compute the packet length */ - packet_len = buffer_len - 6; - if (packet[buffer_len - 1] & 0x20) - packet_len++; - - /* build up the packet */ - packet[0] = 0x0000; - packet[1] = 0x0000; - packet[2] = buffer_len; - packet[3] = buffer_len >> 8; - memcpy(&packet[4], &m_tx[4], 6); - memcpy(&packet[10], &m_tx[10], 6); - memcpy(&packet[16], &m_tx[16], buffer_len - 16); - - /* set the broadcast flag */ - if (is_broadcast) - packet[1] |= 0x40; - - /* pad? */ - if (m_reg[EREG_TCR & 0x0080]) - if (packet_len < 64) - { - memset(&packet[buffer_len], 0, 64 + 6 - buffer_len); - packet[buffer_len - 1] = 0; - buffer_len = 64 + 6; - packet[2] = buffer_len; - packet[3] = buffer_len >> 8; - } + int buffer_len = ((tx_buffer[3] << 8) | tx_buffer[2]) & 0x7ff; - /* signal a receive */ - m_reg[EREG_INTERRUPT] |= EINT_RCV; - m_reg[EREG_FIFO_PORTS] &= ~0x8000; - } + if (VERBOSE & LOG_GENERAL) + { + logerror("TX: "); + for (int i = 4; i < (4 + ETHERNET_ADDR_SIZE); i++) + logerror("%.2X", tx_buffer[i]); + + logerror(" "); + + for (int i = 0; i < (buffer_len - (ETHERNET_ADDR_SIZE + 4)); i++) + logerror("%.2X", tx_buffer[4 + ETHERNET_ADDR_SIZE + i]); + + logerror("--- %d/0x%x bytes\n", buffer_len, buffer_len); } - else 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; + + if ( buffer_len > 4 ) + { + // odd or even sized frame ? + if (tx_buffer[buffer_len-1] & 0x20) + buffer_len--; + else + buffer_len -= 2; + + if (!(m_reg[EREG_TCR] & 0x2002)) + { + // No loopback... Send the frame + if ( !send(&tx_buffer[4], buffer_len-4) ) + { + // FIXME: failed to send the Ethernet packet + //logerror("failed to send Ethernet packet\n"); + //LOG(this,("read_command_port(): !!! failed to send Ethernet packet")); + } } - // 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; + else + { + // TODO loopback mode : Push the frame to the RX FIFO. } - // signal a no transmit - m_reg[EREG_INTERRUPT] &= ~EINT_TX; - // Set a ethernet phy status interrupt - m_reg[EREG_INTERRUPT] |= EINT_EPH; } - update_ethernet_irq(); -} + return 0; +} /*------------------------------------------------- process_command - handle MMU commands @@ -325,66 +516,114 @@ TIMER_CALLBACK_MEMBER(smc91c9x_device::finish_enqueue) void smc91c9x_device::process_command(uint16_t data) { - switch ((data >> 5) & 7) + switch ((data >> 4) & 0xF) { case ECMD_NOP: - if (LOG_ETHERNET) - logerror(" NOP\n"); + LOG(" NOP\n"); break; case ECMD_ALLOCATE: - if (LOG_ETHERNET) - logerror(" ALLOCATE MEMORY FOR TX (%d)\n", (data & 7)); + LOG(" ALLOCATE MEMORY FOR TX (%d)\n", (data & 7)); m_reg[EREG_PNR_ARR] &= ~0xff00; - m_reg[EREG_PNR_ARR] |= m_alloc_count++ << 8; + m_reg[EREG_PNR_ARR] |= (m_alloc_count++ & 0x7F) << 8; m_reg[EREG_INTERRUPT] |= 0x0008; update_ethernet_irq(); break; case ECMD_RESET_MMU: - if (LOG_ETHERNET) - logerror(" RESET MMU\n"); + /* + 0100 + - RESET MMU TO INITIAL STATE - + Frees all memory allocations, clears relevant + interrupts, resets packet FIFO pointers. + */ + + LOG(" RESET MMU\n"); + // Flush fifos. + clear_tx_fifo(); + clear_rx_fifo(); break; - case ECMD_REMOVE: - if (LOG_ETHERNET) - logerror(" REMOVE FRAME FROM RX FIFO\n"); + case ECMD_REMOVE_TOPFRAME_TX: + LOG(" REMOVE FRAME FROM TX FIFO\n"); break; - case ECMD_REMOVE_RELEASE: - if (LOG_ETHERNET) - logerror(" REMOVE AND RELEASE FRAME FROM RX FIFO\n"); + case ECMD_REMOVE_TOPFRAME_RX: + LOG(" REMOVE FRAME FROM RX FIFO\n"); + + case ECMD_REMOVE_RELEASE_TOPFRAME_RX: + LOG(" REMOVE AND RELEASE FRAME FROM RX FIFO (RXI=%d RXO=%d)\n", rx_fifo_in & (ETHER_RX_BUFFERS - 1), rx_fifo_out & (ETHER_RX_BUFFERS - 1)); + m_reg[EREG_INTERRUPT] &= ~EINT_RCV; - if (m_fifo_count > 0) - m_fifo_count--; - if (m_fifo_count > 0) + + if ( (rx_fifo_in & ( ETHER_RX_BUFFERS - 1 ) ) != (rx_fifo_out & ( ETHER_RX_BUFFERS - 1 ) ) ) + rx_fifo_out = ( (rx_fifo_out + 1) & ( ETHER_RX_BUFFERS - 1 ) ); + + if ( (rx_fifo_in & ( ETHER_RX_BUFFERS - 1 ) ) != (rx_fifo_out & ( ETHER_RX_BUFFERS - 1 ) ) ) { - memmove(&m_rx[0], &m_rx[ETHER_BUFFER_SIZE], m_fifo_count * ETHER_BUFFER_SIZE); m_reg[EREG_INTERRUPT] |= EINT_RCV; m_reg[EREG_FIFO_PORTS] &= ~0x8000; } else m_reg[EREG_FIFO_PORTS] |= 0x8000; + update_ethernet_irq(); m_recd++; update_stats(); break; case ECMD_RELEASE_PACKET: - if (LOG_ETHERNET) - logerror(" RELEASE SPECIFIC PACKET\n"); + LOG(" RELEASE SPECIFIC PACKET\n"); break; case ECMD_ENQUEUE_PACKET: - if (LOG_ETHERNET) - logerror(" ENQUEUE TX PACKET\n"); - // Set some delay before tranmit ends - m_tx_timer->adjust(attotime::from_usec(100)); + LOG(" ENQUEUE TX PACKET\n"); + + 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 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; + } + + // signal a no transmit + m_reg[EREG_INTERRUPT] &= ~EINT_TX; + // Set a ethernet phy status interrupt + m_reg[EREG_INTERRUPT] |= EINT_EPH; + + // Flush fifos. + clear_tx_fifo(); + clear_rx_fifo(); + } + else + { + if ( m_reg[EREG_TCR] & 0x0001 ) // TX EN ? + { + send_frame(); + } + } + + update_ethernet_irq(); + break; case ECMD_RESET_FIFOS: - if (LOG_ETHERNET) - logerror(" RESET TX FIFOS\n"); + LOG(" RESET TX FIFOS\n"); + // Flush fifos. + clear_tx_fifo(); + clear_rx_fifo(); + break; } // Set Busy (clear on next read) @@ -407,8 +646,9 @@ READ16_MEMBER( smc91c9x_device::read ) /* determine the effective register */ offset %= 8; - if (offset != EREG_BANK) + if ( offset != EREG_BANK ) offset += 8 * (m_reg[EREG_BANK] & 7); + result = m_reg[offset]; switch (offset) @@ -419,7 +659,7 @@ READ16_MEMBER( smc91c9x_device::read ) break; case EREG_PNR_ARR: - if (ACCESSING_BITS_8_15) + if ( ACCESSING_BITS_8_15 ) { m_reg[EREG_INTERRUPT] &= ~0x0008; update_ethernet_irq(); @@ -429,19 +669,29 @@ READ16_MEMBER( smc91c9x_device::read ) case EREG_DATA_0: /* data register */ case EREG_DATA_1: /* data register */ { - uint8_t *buffer = (m_reg[EREG_POINTER] & 0x8000) ? m_rx : m_tx; + uint8_t *buffer; int addr = m_reg[EREG_POINTER] & 0x7ff; + + if ( m_reg[EREG_POINTER] & 0x8000 ) + { + buffer = &m_rx[(rx_fifo_out & ( ETHER_RX_BUFFERS - 1 )) * ETHER_BUFFER_SIZE]; + } + else + { + buffer = (uint8_t *)&m_tx[(tx_fifo_in & (ETHER_TX_BUFFERS-1))* ETHER_BUFFER_SIZE];; + } + result = buffer[addr++]; - if (ACCESSING_BITS_8_15) + if ( ACCESSING_BITS_8_15 ) result |= buffer[addr++] << 8; - if (m_reg[EREG_POINTER] & 0x4000) + if ( m_reg[EREG_POINTER] & 0x4000 ) m_reg[EREG_POINTER] = (m_reg[EREG_POINTER] & ~0x7ff) | (addr & 0x7ff); break; } } - if (LOG_ETHERNET && offset != EREG_BANK) - logerror("%s:smc91c9x_r(%s) = %04X & %04X\n", machine().describe_context(), ethernet_regname[offset], result, mem_mask); + if (offset != EREG_BANK) + LOG("%s:smc91c9x_r(%s) = %04X & %04X\n", machine().describe_context(), ethernet_regname[offset], result, mem_mask); return result; } @@ -452,95 +702,91 @@ READ16_MEMBER( smc91c9x_device::read ) WRITE16_MEMBER( smc91c9x_device::write ) { - // uint16_t olddata; - /* determine the effective register */ offset %= 8; if (offset != EREG_BANK) offset += 8 * (m_reg[EREG_BANK] & 7); /* update the data generically */ - // olddata = m_reg[offset]; + + if (offset != 7 && 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]; COMBINE_DATA(&m_reg[offset]); - if (LOG_ETHERNET && offset != 7) - logerror("%s:smc91c9x_w(%s) = %04X & %04X\n", machine().describe_context(), ethernet_regname[offset], data, mem_mask); - /* handle it */ switch (offset) { case EREG_TCR: /* transmit control register */ // Setting Tx Enable clears some status and interrupts - if (data & 0x1) { + if ( data & 0x1 ) { m_reg[EREG_EPH_STATUS] &= ~0x420; m_reg[EREG_INTERRUPT] &= ~EINT_EPH; update_ethernet_irq(); } - if (LOG_ETHERNET) - { - if (data & 0x2000) logerror(" EPH LOOP\n"); - if (data & 0x1000) logerror(" STP SQET\n"); - if (data & 0x0800) logerror(" FDUPLX\n"); - if (data & 0x0400) logerror(" MON_CSN\n"); - if (data & 0x0100) logerror(" NOCRC\n"); - if (data & 0x0080) logerror(" PAD_EN\n"); - if (data & 0x0004) logerror(" FORCOL\n"); - if (data & 0x0002) logerror(" LOOP\n"); - if (data & 0x0001) logerror(" 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 */ - if (LOG_ETHERNET) + + if ( data & 0x8000 ) { - if (data & 0x8000) reset(); - if (data & 0x8000) logerror(" SOFT RST\n"); - if (data & 0x4000) logerror(" FILT_CAR\n"); - if (data & 0x0200) logerror(" STRIP CRC\n"); - if (data & 0x0100) logerror(" RXEN\n"); - if (data & 0x0004) logerror(" ALMUL\n"); - if (data & 0x0002) logerror(" PRMS\n"); - if (data & 0x0001) logerror(" RX_ABORT\n"); + clear_rx_fifo(); + clear_tx_fifo(); } - break; - case EREG_CONFIG: /* configuration register */ - if (LOG_ETHERNET) + if ( !(data & 0x0100) ) { - if (data & 0x1000) logerror(" NO WAIT\n"); - if (data & 0x0400) logerror(" FULL STEP\n"); - if (data & 0x0200) logerror(" SET SQLCH\n"); - if (data & 0x0100) logerror(" AUI SELECT\n"); - if (data & 0x0080) logerror(" 16 BIT\n"); - if (data & 0x0040) logerror(" DIS LINK\n"); - if (data & 0x0004) logerror(" INT SEL1\n"); - if (data & 0x0002) logerror(" INT SEL0\n"); + clear_rx_fifo(); } + + 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"); + break; + + case EREG_CONFIG: /* configuration register */ + if (data & 0x1000) LOG(" NO WAIT\n"); + if (data & 0x0400) LOG(" FULL STEP\n"); + if (data & 0x0200) LOG(" SET SQLCH\n"); + if (data & 0x0100) LOG(" AUI SELECT\n"); + if (data & 0x0080) LOG(" 16 BIT\n"); + if (data & 0x0040) LOG(" DIS LINK\n"); + if (data & 0x0004) LOG(" INT SEL1\n"); + if (data & 0x0002) LOG(" INT SEL0\n"); break; case EREG_BASE: /* base address register */ - if (LOG_ETHERNET) - { - logerror(" base = $%04X\n", (data & 0xe000) | ((data & 0x1f00) >> 3)); - logerror(" romsize = %d\n", ((data & 0xc0) >> 6)); - logerror(" romaddr = $%05X\n", ((data & 0x3e) << 13)); - } + 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 (LOG_ETHERNET) - { - if (data & 0x4000) logerror(" RCV_BAD\n"); - if (data & 0x2000) logerror(" PWRDN\n"); - if (data & 0x0800) logerror(" AUTO RELEASE\n"); - if (data & 0x0080) logerror(" LE ENABLE\n"); - if (data & 0x0040) logerror(" CR ENABLE\n"); - if (data & 0x0020) logerror(" TE ENABLE\n"); - if (data & 0x0004) logerror(" EEPROM SELECT\n"); - if (data & 0x0002) logerror(" RELOAD\n"); - if (data & 0x0001) logerror(" STORE\n"); - } + 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"); break; case EREG_MMU_COMMAND: /* command register */ @@ -550,12 +796,22 @@ WRITE16_MEMBER( smc91c9x_device::write ) case EREG_DATA_0: /* data register */ case EREG_DATA_1: /* data register */ { - uint8_t *buffer = (m_reg[EREG_POINTER] & 0x8000) ? m_rx : m_tx; + uint8_t *buffer; int addr = m_reg[EREG_POINTER] & 0x7ff; + + if ( m_reg[EREG_POINTER] & 0x8000 ) + { + buffer = &m_rx[(rx_fifo_out & ( ETHER_RX_BUFFERS - 1 )) * ETHER_BUFFER_SIZE]; + } + else + { + buffer = (uint8_t *)&m_tx[(tx_fifo_in & (ETHER_TX_BUFFERS-1))* ETHER_BUFFER_SIZE];; + } + buffer[addr++] = data; - if (ACCESSING_BITS_8_15) + if ( ACCESSING_BITS_8_15 ) buffer[addr++] = data >> 8; - if (m_reg[EREG_POINTER] & 0x4000) + if ( m_reg[EREG_POINTER] & 0x4000 ) m_reg[EREG_POINTER] = (m_reg[EREG_POINTER] & ~0x7ff) | (addr & 0x7ff); break; } @@ -563,7 +819,7 @@ WRITE16_MEMBER( smc91c9x_device::write ) case EREG_INTERRUPT: m_reg[EREG_INTERRUPT] &= ~(data & 0x56); // Need to clear tx int here for vegas cartfury - if (m_reg[EREG_FIFO_PORTS] & 0x0080) + if ( m_reg[EREG_FIFO_PORTS] & 0x0080 ) m_reg[EREG_INTERRUPT] &= ~EINT_TX; update_ethernet_irq(); break; |