From 1feb45e4c03683c9af91d06c38362819601786b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20DEL=20NERO?= Date: Wed, 1 Nov 2017 14:35:40 +0100 Subject: [PATCH 1/2] Fix DS128X/DS1288X main frequency divider. The output frequency must be fixed to 1HZ with these devices. In the MC146818, DV2-DV0 were used to select the input frequency to provide a proper time base. Since the DS12885/87 and DS1685/87 use only the 32.768kHz crystal these 3 bits are used to turn the oscillator on or off and to reset the countdown chain. There are not used anymore to select the main clock divider value. --- src/devices/machine/ds128x.cpp | 12 +++++-- src/devices/machine/ds128x.h | 1 + src/devices/machine/mc146818.cpp | 61 +++++++++++++++++++------------- src/devices/machine/mc146818.h | 3 +- 4 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/devices/machine/ds128x.cpp b/src/devices/machine/ds128x.cpp index 7c824aba638..7a397aab303 100644 --- a/src/devices/machine/ds128x.cpp +++ b/src/devices/machine/ds128x.cpp @@ -3,8 +3,6 @@ #include "emu.h" #include "ds128x.h" -/// TODO: Only DV2/DV1/DV0 == 0/1/0 is supported as the chip only has a 15 stage divider and not 22. - DEFINE_DEVICE_TYPE(DS12885, ds12885_device, "ds12885", "DS12885 RTC/NVRAM") //------------------------------------------------- @@ -15,3 +13,13 @@ ds12885_device::ds12885_device(const machine_config &mconfig, const char *tag, d : mc146818_device(mconfig, DS12885, tag, owner, clock) { } + +int ds12885_device::get_timer_bypass() +{ + if( !( m_data[REG_A] & REG_A_DV0 ) ) //DV0 must be 0 for timekeeping + { + return 7; // Fixed at 1 Hz with clock at 32768Hz + } + + return 22; // No tick +} diff --git a/src/devices/machine/ds128x.h b/src/devices/machine/ds128x.h index e74227e58b1..e525d7a79d3 100644 --- a/src/devices/machine/ds128x.h +++ b/src/devices/machine/ds128x.h @@ -18,6 +18,7 @@ public: protected: virtual int data_size() override { return 128; } + virtual int get_timer_bypass() override; }; // device type definition diff --git a/src/devices/machine/mc146818.cpp b/src/devices/machine/mc146818.cpp index 4cdb4f37b5e..afe912a7d0d 100644 --- a/src/devices/machine/mc146818.cpp +++ b/src/devices/machine/mc146818.cpp @@ -412,31 +412,7 @@ void mc146818_device::update_timer() { int bypass; - switch (m_data[REG_A] & (REG_A_DV2 | REG_A_DV1 | REG_A_DV0)) - { - case 0: - bypass = 0; - break; - - case REG_A_DV0: - bypass = 2; - break; - - case REG_A_DV1: - bypass = 7; - break; - - case REG_A_DV2 | REG_A_DV1: - case REG_A_DV2 | REG_A_DV1 | REG_A_DV0: - bypass = 22; - break; - - default: - // TODO: other combinations of divider bits are used for test purposes only - bypass = 22; - break; - } - + bypass = get_timer_bypass(); attotime update_period = attotime::never; attotime update_interval = attotime::never; @@ -472,6 +448,41 @@ void mc146818_device::update_timer() m_periodic_timer->adjust(periodic_period, 0, periodic_interval); } +//--------------------------------------------------------------- +// get_timer_bypass - get main clock divisor based on A register +//--------------------------------------------------------------- + +int mc146818_device::get_timer_bypass() +{ + int bypass; + + switch (m_data[REG_A] & (REG_A_DV2 | REG_A_DV1 | REG_A_DV0)) + { + case 0: + bypass = 0; + break; + + case REG_A_DV0: + bypass = 2; + break; + + case REG_A_DV1: + bypass = 7; + break; + + case REG_A_DV2 | REG_A_DV1: + case REG_A_DV2 | REG_A_DV1 | REG_A_DV0: + bypass = 22; + break; + + default: + // TODO: other combinations of divider bits are used for test purposes only + bypass = 22; + break; + } + + return bypass; +} //------------------------------------------------- // update_irq - Update irq based on B & C register diff --git a/src/devices/machine/mc146818.h b/src/devices/machine/mc146818.h index 408f9f717d8..85256106abf 100644 --- a/src/devices/machine/mc146818.h +++ b/src/devices/machine/mc146818.h @@ -91,7 +91,6 @@ protected: virtual int data_size() { return 64; } -private: enum { REG_SECONDS = 0, @@ -153,7 +152,7 @@ private: void set_base_datetime(); void update_irq(); void update_timer(); - + virtual int get_timer_bypass(); int get_seconds(); void set_seconds(int seconds); int get_minutes(); From b89717f97af3ecdae99bcaa1a73bf3210e963f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20DEL=20NERO?= Date: Sun, 12 Nov 2017 22:34:41 +0100 Subject: [PATCH 2/2] SMC91C9X Ethernet controller now working in connected mode with WinPcap or TUN/TAP tunnel. --- src/devices/machine/smc91c9x.cpp | 504 ++++++++++++++++++++++++------- src/devices/machine/smc91c9x.h | 28 +- 2 files changed, 424 insertions(+), 108 deletions(-) diff --git a/src/devices/machine/smc91c9x.cpp b/src/devices/machine/smc91c9x.cpp index ee5214d2323..12fc62d0ffa 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 **************************************************************************/ @@ -68,14 +68,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 +100,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 +119,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 +137,42 @@ void smc91c9x_device::device_start() void smc91c9x_device::device_reset() { + unsigned char i; + const unsigned char * mac; + memset(m_reg, 0, sizeof(m_reg)); + + memset(m_rx, 0, sizeof(m_rx)); + memset(m_tx, 0, sizeof(m_tx)); + memset(m_regmask, 0, sizeof(m_regmask)); 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(); + + mac = (const unsigned char *)get_mac(); + + if (LOG_ETHERNET) + { + logerror("MAC : "); + for(i=0;iadjust(attotime::never); } - 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) @@ -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,194 @@ 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; + memset(m_tx, 0, sizeof(m_tx)); +} + +void smc91c9x_device::clear_rx_fifo() +{ + rx_fifo_in = 0; + rx_fifo_out = 0; + memset(m_rx, 0, sizeof(m_rx)); +} + +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]; + + if (LOG_ETHERNET) + logerror("\r\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 (LOG_ETHERNET) + { + for(i=0;i= ETHERNET_ADDR_SIZE && LOG_ETHERNET) + { + logerror("RX: "); + for(i=0;i> 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{ + if (LOG_ETHERNET) + logerror("Rejected ! Fifo Full ?"); + } + + update_ethernet_irq(); + + if (LOG_ETHERNET) + logerror("\r\n"); +} + /*************************************************************************** INTERNAL HELPERS ***************************************************************************/ @@ -233,99 +451,92 @@ void smc91c9x_device::update_stats() /*------------------------------------------------- - 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 i; + uint8_t * tx_buffer; int 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 ); + + 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 = ((tx_buffer[3] << 8) | tx_buffer[2]) & 0x7ff; + + if (LOG_ETHERNET) + { + logerror("TX: "); + for(i=4;i<4+ETHERNET_ADDR_SIZE;i++) { - 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; + logerror("%.2X",tx_buffer[i]); + } - /* compute the packet length */ - packet_len = buffer_len - 6; - if (packet[buffer_len - 1] & 0x20) - packet_len++; + logerror(" "); - /* 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); + for(i=0;i> 8; - } + if(buffer_len>4) + { + // odd or even sized frame ? + if(tx_buffer[buffer_len-1] & 0x20) + buffer_len--; + else + buffer_len -= 2; - /* signal a receive */ - m_reg[EREG_INTERRUPT] |= EINT_RCV; - m_reg[EREG_FIFO_PORTS] &= ~0x8000; + 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\r\n"); + //LOG(this,("read_command_port(): !!! failed to send Ethernet packet")); + } + } + else + { + // TODO loopback mode : Push the frame to the RX FIFO. } } - 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; - } - // 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; - } - update_ethernet_irq(); + + return 0; } - /*------------------------------------------------- process_command - handle MMU commands -------------------------------------------------*/ void smc91c9x_device::process_command(uint16_t data) { - switch ((data >> 5) & 7) + switch ((data >> 4) & 0xF) { case ECMD_NOP: if (LOG_ETHERNET) @@ -336,35 +547,52 @@ void smc91c9x_device::process_command(uint16_t data) if (LOG_ETHERNET) logerror(" 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: + /* + 0100 + - RESET MMU TO INITIAL STATE - + Frees all memory allocations, clears relevant + interrupts, resets packet FIFO pointers. + */ + if (LOG_ETHERNET) logerror(" RESET MMU\n"); + // Flush fifos. + clear_tx_fifo(); + clear_rx_fifo(); break; - case ECMD_REMOVE: + case ECMD_REMOVE_TOPFRAME_TX: + if (LOG_ETHERNET) + logerror(" REMOVE FRAME FROM TX FIFO\n"); + break; + + case ECMD_REMOVE_TOPFRAME_RX: if (LOG_ETHERNET) logerror(" REMOVE FRAME FROM RX FIFO\n"); - break; - case ECMD_REMOVE_RELEASE: + case ECMD_REMOVE_RELEASE_TOPFRAME_RX: if (LOG_ETHERNET) - logerror(" REMOVE AND RELEASE FRAME FROM RX FIFO\n"); + logerror(" 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(); @@ -378,13 +606,53 @@ void smc91c9x_device::process_command(uint16_t data) 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)); + + 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"); + // Flush fifos. + clear_tx_fifo(); + clear_rx_fifo(); + break; } // Set Busy (clear on next read) @@ -409,6 +677,7 @@ READ16_MEMBER( smc91c9x_device::read ) offset %= 8; if (offset != EREG_BANK) offset += 8 * (m_reg[EREG_BANK] & 7); + result = m_reg[offset]; switch (offset) @@ -429,8 +698,18 @@ 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) result |= buffer[addr++] << 8; @@ -441,7 +720,7 @@ READ16_MEMBER( smc91c9x_device::read ) } if (LOG_ETHERNET && offset != EREG_BANK) - logerror("%s:smc91c9x_r(%s) = %04X & %04X\n", machine().describe_context(), ethernet_regname[offset], result, mem_mask); + logerror("%s:smc91c9x_r(%s) = %04X & %04X\n", (machine().describe_context()).c_str(), ethernet_regname[offset], result, mem_mask); return result; } @@ -452,21 +731,19 @@ 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 (LOG_ETHERNET && offset != 7 && offset < sizeof(m_reg)) + logerror("%s:smc91c9x_w(%s) = [%04X]<-%04X & (%04X & %04X)\n", (machine().describe_context()).c_str(), 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) { @@ -477,6 +754,7 @@ WRITE16_MEMBER( smc91c9x_device::write ) m_reg[EREG_INTERRUPT] &= ~EINT_EPH; update_ethernet_irq(); } + if (LOG_ETHERNET) { if (data & 0x2000) logerror(" EPH LOOP\n"); @@ -492,6 +770,18 @@ WRITE16_MEMBER( smc91c9x_device::write ) break; case EREG_RCR: /* receive control register */ + + if (data & 0x8000) + { + clear_rx_fifo(); + clear_tx_fifo(); + } + + if (!(data & 0x0100)) + { + clear_rx_fifo(); + } + if (LOG_ETHERNET) { if (data & 0x8000) reset(); @@ -550,8 +840,18 @@ 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) buffer[addr++] = data >> 8; diff --git a/src/devices/machine/smc91c9x.h b/src/devices/machine/smc91c9x.h index 78627fb58d3..f305efef23e 100644 --- a/src/devices/machine/smc91c9x.h +++ b/src/devices/machine/smc91c9x.h @@ -4,7 +4,7 @@ SMC91C9X ethernet controller implementation - by Aaron Giles + by Aaron Giles, Jean-François DEL NERO **************************************************************************/ @@ -15,7 +15,7 @@ TYPE DEFINITIONS ***************************************************************************/ -class smc91c9x_device : public device_t +class smc91c9x_device : public device_t,public device_network_interface { public: template static devcb_base &set_irq_callback(device_t &device, Object &&cb) { return downcast(device).m_irq_handler.set_callback(std::forward(cb)); } @@ -23,6 +23,8 @@ public: DECLARE_READ16_MEMBER( read ); DECLARE_WRITE16_MEMBER( write ); + virtual void recv_cb(uint8_t *data, int length) override; + protected: smc91c9x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); @@ -32,7 +34,9 @@ protected: private: static constexpr unsigned ETHER_BUFFER_SIZE = 2048; - static constexpr unsigned ETHER_RX_BUFFERS = 4; + static constexpr unsigned ETHER_RX_BUFFERS = 16; + static constexpr unsigned ETHER_TX_BUFFERS = 16; + static constexpr unsigned ETHERNET_ADDR_SIZE = 6; // internal state devcb_write_line m_irq_handler; @@ -52,18 +56,30 @@ private: /* transmit/receive FIFOs */ uint8_t m_fifo_count; + + uint32_t rx_fifo_out; + uint32_t rx_fifo_in; uint8_t m_rx[ETHER_BUFFER_SIZE * ETHER_RX_BUFFERS]; - uint8_t m_tx[ETHER_BUFFER_SIZE]; + + uint32_t tx_fifo_out; + uint32_t tx_fifo_in; + uint8_t m_tx[ETHER_BUFFER_SIZE * ETHER_TX_BUFFERS]; /* counters */ uint32_t m_sent; uint32_t m_recd; + int ethernet_packet_is_for_me(const uint8_t mac_address[]); + int is_broadcast(uint8_t mac_address[]); + void update_ethernet_irq(); void update_stats(); - TIMER_CALLBACK_MEMBER(finish_enqueue); + void process_command(uint16_t data); - emu_timer* m_tx_timer; + void clear_tx_fifo(); + void clear_rx_fifo(); + + int send_frame(); };