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