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.cpp115
1 files changed, 71 insertions, 44 deletions
diff --git a/src/devices/machine/smc91c9x.cpp b/src/devices/machine/smc91c9x.cpp
index 359a69eefdb..ea153bc2d58 100644
--- a/src/devices/machine/smc91c9x.cpp
+++ b/src/devices/machine/smc91c9x.cpp
@@ -10,14 +10,16 @@
#include "emu.h"
#include "smc91c9x.h"
-#include <sstream>
+
+#include "multibyte.h"
+
#include <iomanip>
+#include <sstream>
/***************************************************************************
DEBUGGING
***************************************************************************/
-#define LOG_GENERAL (1U << 0)
#define LOG_PACKETS (1U << 1)
#define LOG_TX (1U << 2)
#define LOG_RX (1U << 3)
@@ -62,7 +64,7 @@ smc91c96_device::smc91c96_device(const machine_config &mconfig, const char *tag,
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)
+ , device_network_interface(mconfig, *this, 10)
, m_device_type(device_type)
, m_num_ebuf(16)
, m_irq_handler(*this)
@@ -71,7 +73,6 @@ smc91c9x_device::smc91c9x_device(const machine_config &mconfig, device_type type
}
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
@@ -83,9 +84,7 @@ void smc91c9x_device::device_start()
m_buffer = std::make_unique<u8[]>(ETHER_BUFFER_SIZE * m_num_ebuf);
// TX timer
- m_tx_poll = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(smc91c9x_device::tx_poll), this));
-
- m_irq_handler.resolve_safe();
+ m_tx_poll = timer_alloc(FUNC(smc91c9x_device::tx_poll), this);
// These registers don't get cleared on reset
m_reg[B1_CONFIG] = 0x0030; m_regmask[B1_CONFIG] = 0x17c6;
@@ -95,12 +94,19 @@ void smc91c9x_device::device_start()
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
+ // Revision and MIR is based on chip type
m_regmask[B3_REVISION] = 0x0000;
+ m_regmask[B0_MIR] = 0x0000;
if (m_device_type == dev_type::SMC91C94)
+ {
m_reg[B3_REVISION] = 0x3345;
+ m_reg[B0_MIR] = 0x1212;
+ }
else if (m_device_type == dev_type::SMC91C96)
+ {
m_reg[B3_REVISION] = 0x3346;
+ m_reg[B0_MIR] = 0x1818;
+ }
else
fatalerror("device_start: Unknown device type\n");
@@ -147,13 +153,12 @@ void smc91c9x_device::device_reset()
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_TCR] = 0x0000; m_regmask[B0_TCR] = 0xbd87;
+ m_reg[B0_EPH_STATUS] = 0x4000; 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[B0_BANK] = 0x3330; 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;
@@ -174,6 +179,8 @@ void smc91c9x_device::device_reset()
m_reg[B3_ERCV] = 0x331f; m_regmask[B3_ERCV] = 0x009f;
+ set_promisc(false);
+
update_ethernet_irq();
// Reset MMU
@@ -186,6 +193,10 @@ void smc91c9x_device::mmu_reset()
m_alloc_rx = 0;
m_alloc_tx = 0;
+ // The register defaults to the MEMORY SIZE upon reset or upon the RESET MMU command.
+ m_reg[B0_MIR] &= 0xff;
+ m_reg[B0_MIR] |= m_reg[B0_MIR] << 8;
+
// Reset fifos.
reset_tx_fifos();
reset_completed_rx();
@@ -394,16 +405,8 @@ int smc91c9x_device::recv_start_cb(u8 *buf, int length)
return 0;
}
- // 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);
-
- return 0;
- }
-
// Check for active transmission
- if (m_tx_active)
+ if (m_tx_active && !(m_reg[B0_TCR] & FDSE))
{
// TODO: Update collision counters
LOGMASKED(LOG_RX, "transmit active COLLISION, rx packet length %d discarded\n", length);
@@ -524,7 +527,7 @@ void smc91c9x_device::recv_complete_cb(int result)
TIMER_CALLBACK_MEMBER(smc91c9x_device::tx_poll)
{
// Check for active RX and delay if necessary
- if (m_rx_active)
+ if (m_rx_active && !(m_reg[B0_TCR] & FDSE))
{
// TODO: Implement correct CSMA/CD algorithm
m_tx_poll->adjust(attotime::from_usec(40));
@@ -557,13 +560,12 @@ TIMER_CALLBACK_MEMBER(smc91c9x_device::tx_poll)
tx_buffer[length++] = 0x00;
// Add CRC
- // TODO: Calculate CRC
- if (1 && ((control & EBUF_CRC) || !(m_reg[B0_TCR] & NOCRC)))
+ if (((control & EBUF_CRC) || !(m_reg[B0_TCR] & NOCRC)))
{
- tx_buffer[length++] = 0x11;
- tx_buffer[length++] = 0x22;
- tx_buffer[length++] = 0x33;
- tx_buffer[length++] = 0x44;
+ u32 crc = util::crc32_creator::simple(tx_buffer + 4, length - 4);
+
+ put_u32le(&tx_buffer[length], crc);
+ length += 4;
}
// Remove status, length
@@ -590,7 +592,7 @@ TIMER_CALLBACK_MEMBER(smc91c9x_device::tx_poll)
if (m_reg[B0_TCR] & (EPH_LOOP | LOOP))
send_complete_cb(length);
else
- send(&tx_buffer[4], length);
+ send(&tx_buffer[4], length, 4);
}
}
@@ -655,11 +657,19 @@ void smc91c9x_device::send_complete_cb(int result)
m_reg[B2_INTERRUPT] |= EINT_EPH;
}
- // Update status in the transmit word
- *(u16*)&tx_buffer[0] = m_reg[B0_EPH_STATUS];
+ if (m_reg[B1_CONTROL] & AUTO_RELEASE)
+ {
+ alloc_release(packet_num);
+ }
+ else
+ {
+ // Update status in the transmit word
+ *(u16*)&tx_buffer[0] = m_reg[B0_EPH_STATUS];
+
+ // Push the packet number onto the tx completion fifo
+ push_completed_tx(packet_num);
+ }
- // Push the packet number onto the tx completion fifo
- push_completed_tx(packet_num);
update_ethernet_irq();
@@ -729,7 +739,7 @@ void smc91c9x_device::process_command(uint16_t data)
LOG(" REMOVE AND RELEASE FRAME FROM RX FIFO (PACK_NUM=%d)\n", curr_completed_rx());
// Release memory allocation
alloc_release(curr_completed_rx());
- // Fall through
+ [[fallthrough]];
case ECMD_REMOVE_TOPFRAME_RX:
LOG(" REMOVE FRAME FROM RX FIFO\n");
// remove entry from rx completion queue
@@ -791,7 +801,7 @@ void smc91c9x_device::process_command(uint16_t data)
smc91c9x_r - handle a read from the device
-------------------------------------------------*/
-READ16_MEMBER( smc91c9x_device::read )
+u16 smc91c9x_device::read(offs_t offset, u16 mem_mask)
{
uint32_t result;
@@ -839,9 +849,11 @@ READ16_MEMBER( smc91c9x_device::read )
if ( m_reg[B2_POINTER] & 0x8000 )
buffer = &m_buffer[curr_completed_rx() * ETHER_BUFFER_SIZE];
else
- buffer = &m_buffer[(m_reg[B2_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];;
+ buffer = &m_buffer[(m_reg[B2_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];
- result = buffer[addr++];
+ result = 0;
+ if ( ACCESSING_BITS_0_7 )
+ result = buffer[addr++];
if ( ACCESSING_BITS_8_15 )
result |= buffer[addr++] << 8;
if ( m_reg[B2_POINTER] & 0x4000 )
@@ -860,7 +872,7 @@ READ16_MEMBER( smc91c9x_device::read )
smc91c9x_w - handle a write to the device
-------------------------------------------------*/
-WRITE16_MEMBER( smc91c9x_device::write )
+void smc91c9x_device::write(offs_t offset, u16 data, u16 mem_mask)
{
/* determine the effective register */
offset %= 8;
@@ -872,8 +884,10 @@ WRITE16_MEMBER( smc91c9x_device::write )
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]);
+ const uint16_t old_reg = m_reg[offset];
mem_mask &= m_regmask[offset];
COMBINE_DATA(&m_reg[offset]);
+ const uint16_t new_reg = m_reg[offset];
/* handle it */
switch (offset)
@@ -911,7 +925,7 @@ WRITE16_MEMBER( smc91c9x_device::write )
reset();
}
- if ( !(data & RXEN) )
+ if ( (old_reg & RXEN) && !(new_reg & RXEN))
{
reset_completed_rx();
}
@@ -921,6 +935,11 @@ WRITE16_MEMBER( smc91c9x_device::write )
m_reg[B0_EPH_STATUS] |= LINK_OK;
}
+ if ((old_reg ^ new_reg) & PRMS)
+ {
+ set_promisc(new_reg & PRMS);
+ }
+
if (VERBOSE & LOG_GENERAL)
{
if (data & SOFT_RST) LOG(" SOFT RST\n");
@@ -951,14 +970,21 @@ WRITE16_MEMBER( smc91c9x_device::write )
break;
case B1_IA4_5:
- set_promisc(m_reg[B0_RCR] & PRMS);
- set_mac((char *)&m_reg[B1_IA0_1]);
+ if ( ACCESSING_BITS_8_15 )
+ {
+ set_promisc(m_reg[B0_RCR] & PRMS);
+ u8 mac[6];
+ put_u16le(&mac[0], m_reg[B1_IA0_1]);
+ put_u16le(&mac[2], m_reg[B1_IA2_3]);
+ put_u16le(&mac[4], m_reg[B1_IA4_5]);
+ set_mac(mac);
+ }
break;
case B1_CONTROL: /* control register */
// Clearing LE_EN clears interrupt from LINK_OK status change
- if (!(data & LE_ENABLE))
+ if ( (old_reg & LE_ENABLE) && !(new_reg & LE_ENABLE))
{
m_reg[B2_INTERRUPT] &= ~EINT_EPH;
update_ethernet_irq();
@@ -1000,9 +1026,10 @@ WRITE16_MEMBER( smc91c9x_device::write )
if (m_reg[B2_POINTER] & RCV)
buffer = &m_buffer[curr_completed_rx() * ETHER_BUFFER_SIZE];
else
- buffer = &m_buffer[(m_reg[B2_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];;
+ buffer = &m_buffer[(m_reg[B2_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];
- buffer[addr++] = data;
+ if ( ACCESSING_BITS_0_7 )
+ buffer[addr++] = data;
if ( ACCESSING_BITS_8_15 )
buffer[addr++] = data >> 8;
if ( m_reg[B2_POINTER] & AUTO_INCR)