diff options
Diffstat (limited to 'src/devices/machine/smc91c9x.cpp')
-rw-r--r-- | src/devices/machine/smc91c9x.cpp | 115 |
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) |