mirror of
https://github.com/holub/mame
synced 2025-06-29 15:38:53 +03:00
smc91c9x: WIP Fixed loopback and added proper MMU handling. (nw)
This commit is contained in:
parent
f9ce368faa
commit
e6cd405fde
@ -15,7 +15,8 @@
|
|||||||
|
|
||||||
#include "emu.h"
|
#include "emu.h"
|
||||||
#include "smc91c9x.h"
|
#include "smc91c9x.h"
|
||||||
|
// Needed for netdev_count???
|
||||||
|
#include "osdnet.h"
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
@ -120,17 +121,23 @@ smc91c9x_device::smc91c9x_device(const machine_config &mconfig, device_type type
|
|||||||
|
|
||||||
void smc91c9x_device::device_start()
|
void smc91c9x_device::device_start()
|
||||||
{
|
{
|
||||||
|
// TX timer
|
||||||
|
m_tx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(smc91c9x_device::send_frame), this));
|
||||||
|
|
||||||
m_irq_handler.resolve_safe();
|
m_irq_handler.resolve_safe();
|
||||||
|
|
||||||
/* register ide states */
|
/* register ide states */
|
||||||
save_item(NAME(m_reg));
|
save_item(NAME(m_reg));
|
||||||
save_item(NAME(m_regmask));
|
save_item(NAME(m_regmask));
|
||||||
save_item(NAME(m_irq_state));
|
save_item(NAME(m_irq_state));
|
||||||
save_item(NAME(m_alloc_count));
|
save_item(NAME(m_buffer));
|
||||||
save_item(NAME(m_rx));
|
|
||||||
save_item(NAME(m_tx));
|
|
||||||
save_item(NAME(m_sent));
|
save_item(NAME(m_sent));
|
||||||
save_item(NAME(m_recd));
|
save_item(NAME(m_recd));
|
||||||
|
save_item(NAME(m_alloc_rx));
|
||||||
|
save_item(NAME(m_alloc_tx));
|
||||||
|
// TODO: Need to save these
|
||||||
|
//save_item(NAME(m_comp_rx));
|
||||||
|
//save_item(NAME(m_comp_tx));
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
@ -141,37 +148,13 @@ void smc91c9x_device::device_reset()
|
|||||||
{
|
{
|
||||||
std::fill(std::begin(m_reg), std::end(m_reg), 0);
|
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);
|
std::fill(std::begin(m_regmask), std::end(m_regmask), 0);
|
||||||
|
|
||||||
m_irq_state = 0;
|
m_irq_state = 0;
|
||||||
m_alloc_count = 0;
|
|
||||||
rx_fifo_out = 0;
|
|
||||||
rx_fifo_in = 0;
|
|
||||||
|
|
||||||
tx_fifo_out = 0;
|
|
||||||
tx_fifo_in = 0;
|
|
||||||
|
|
||||||
m_sent = 0;
|
m_sent = 0;
|
||||||
m_recd = 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_TCR] = 0x0000; m_regmask[EREG_TCR] = 0x3d87;
|
||||||
m_reg[EREG_EPH_STATUS] = 0x0000; m_regmask[EREG_EPH_STATUS] = 0x0000;
|
m_reg[EREG_EPH_STATUS] = 0x0000; m_regmask[EREG_EPH_STATUS] = 0x0000;
|
||||||
m_reg[EREG_RCR] = 0x0000; m_regmask[EREG_RCR] = 0xc307;
|
m_reg[EREG_RCR] = 0x0000; m_regmask[EREG_RCR] = 0xc307;
|
||||||
@ -188,11 +171,6 @@ void smc91c9x_device::device_reset()
|
|||||||
m_reg[EREG_IA2_3] = 0x12F7; m_regmask[EREG_IA2_3] = 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;
|
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_GENERAL_PURP] = 0x0000; m_regmask[EREG_GENERAL_PURP] = 0xffff;
|
||||||
m_reg[EREG_CONTROL] = 0x0100; m_regmask[EREG_CONTROL] = 0x68e7;
|
m_reg[EREG_CONTROL] = 0x0100; m_regmask[EREG_CONTROL] = 0x68e7;
|
||||||
|
|
||||||
@ -209,12 +187,51 @@ void smc91c9x_device::device_reset()
|
|||||||
m_reg[EREG_MT4_5] = 0x0000; m_regmask[EREG_MT4_5] = 0xffff;
|
m_reg[EREG_MT4_5] = 0x0000; m_regmask[EREG_MT4_5] = 0xffff;
|
||||||
m_reg[EREG_MT6_7] = 0x0000; m_regmask[EREG_MT6_7] = 0xffff;
|
m_reg[EREG_MT6_7] = 0x0000; m_regmask[EREG_MT6_7] = 0xffff;
|
||||||
m_reg[EREG_MGMT] = 0x3030; m_regmask[EREG_MGMT] = 0x0f0f;
|
m_reg[EREG_MGMT] = 0x3030; m_regmask[EREG_MGMT] = 0x0f0f;
|
||||||
m_reg[EREG_REVISION] = 0x3340; m_regmask[EREG_REVISION] = 0x0000;
|
m_reg[EREG_REVISION] = 0x3345; m_regmask[EREG_REVISION] = 0x0000;
|
||||||
m_reg[EREG_ERCV] = 0x331f; m_regmask[EREG_ERCV] = 0x009f;
|
m_reg[EREG_ERCV] = 0x331f; m_regmask[EREG_ERCV] = 0x009f;
|
||||||
|
|
||||||
update_ethernet_irq();
|
update_ethernet_irq();
|
||||||
|
m_tx_timer->reset();
|
||||||
|
|
||||||
|
// Setup real network if enabled
|
||||||
|
if (netdev_count()) {
|
||||||
|
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);
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset MMU
|
||||||
|
mmu_reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void smc91c9x_device::mmu_reset()
|
||||||
|
{
|
||||||
|
// Reset MMU allocations
|
||||||
|
m_alloc_rx = 0;
|
||||||
|
m_alloc_tx = 0;
|
||||||
|
// Reset completion FIFOs
|
||||||
|
while (!m_comp_tx.empty())
|
||||||
|
m_comp_tx.pop();
|
||||||
|
while (!m_comp_rx.empty())
|
||||||
|
m_comp_rx.pop();
|
||||||
|
|
||||||
|
// Flush fifos.
|
||||||
|
clear_tx_fifo();
|
||||||
|
clear_rx_fifo();
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_DEVICE_TYPE(SMC91C94, smc91c94_device, "smc91c94", "SMC91C94 Ethernet Controller")
|
DEFINE_DEVICE_TYPE(SMC91C94, smc91c94_device, "smc91c94", "SMC91C94 Ethernet Controller")
|
||||||
|
|
||||||
@ -230,18 +247,38 @@ smc91c96_device::smc91c96_device(const machine_config &mconfig, const char *tag,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool smc91c9x_device::alloc_req(const int tx, int &packet_num)
|
||||||
|
{
|
||||||
|
u32 curr_alloc = m_alloc_rx | m_alloc_tx;
|
||||||
|
|
||||||
|
for (int index = 0; index < ETHER_BUFFERS; index++) {
|
||||||
|
if (!(curr_alloc & (1 << index))) {
|
||||||
|
packet_num = index;
|
||||||
|
if (tx) {
|
||||||
|
m_alloc_tx |= 1 << index;
|
||||||
|
} else {
|
||||||
|
m_alloc_rx |= 1 << index;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void smc91c9x_device::alloc_release(const int packet_num)
|
||||||
|
{
|
||||||
|
int clear_mask = ~(1 << packet_num);
|
||||||
|
m_alloc_tx &= clear_mask;
|
||||||
|
m_alloc_rx &= clear_mask;
|
||||||
|
}
|
||||||
|
|
||||||
void smc91c9x_device::clear_tx_fifo()
|
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()
|
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 smc91c9x_device::is_broadcast(uint8_t mac_address[])
|
||||||
@ -268,22 +305,14 @@ int smc91c9x_device::ethernet_packet_is_for_me(const uint8_t mac_address[])
|
|||||||
// 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
|
// 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;
|
int i;
|
||||||
uint8_t local_address[ETHERNET_ADDR_SIZE];
|
|
||||||
|
|
||||||
LOG("\n");
|
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)
|
if (VERBOSE & LOG_GENERAL)
|
||||||
{
|
{
|
||||||
for ( i = 0 ; i < ETHERNET_ADDR_SIZE ; i++ )
|
for ( i = 0 ; i < ETHERNET_ADDR_SIZE ; i++ )
|
||||||
{
|
{
|
||||||
logerror("%.2X",local_address[i]);
|
logerror("%.2X", ((u8 *)&m_reg[EREG_IA0_1])[i]);
|
||||||
}
|
}
|
||||||
logerror("=");
|
logerror("=");
|
||||||
for ( i = 0 ; i < ETHERNET_ADDR_SIZE ; i++ )
|
for ( i = 0 ; i < ETHERNET_ADDR_SIZE ; i++ )
|
||||||
@ -300,7 +329,7 @@ int smc91c9x_device::ethernet_packet_is_for_me(const uint8_t mac_address[])
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(mac_address, local_address, ETHERNET_ADDR_SIZE) == 0)
|
if (memcmp(mac_address, &m_reg[EREG_IA0_1], ETHERNET_ADDR_SIZE) == 0)
|
||||||
{
|
{
|
||||||
LOG(" -- Address Match\n");
|
LOG(" -- Address Match\n");
|
||||||
return 1;
|
return 1;
|
||||||
@ -335,7 +364,7 @@ void smc91c9x_device::recv_cb(uint8_t *data, int length)
|
|||||||
logerror(" - IsForMe %d - %d/0x%x bytes\n", isforme, length, length);
|
logerror(" - IsForMe %d - %d/0x%x bytes\n", isforme, length, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (length < ETHERNET_ADDR_SIZE || !isforme) && !(m_reg[EREG_RCR] & 0x0100) )
|
if ( (length < ETHERNET_ADDR_SIZE || !isforme) && !(m_reg[EREG_RCR] & 0x0102) )
|
||||||
{
|
{
|
||||||
LOG("\n");
|
LOG("\n");
|
||||||
|
|
||||||
@ -345,13 +374,18 @@ void smc91c9x_device::recv_cb(uint8_t *data, int length)
|
|||||||
|
|
||||||
/* signal a receive */
|
/* signal a receive */
|
||||||
|
|
||||||
|
// Try to request a packet number
|
||||||
|
int packet_num;
|
||||||
|
if (!alloc_req(0, packet_num)) {
|
||||||
|
logerror("recv_cb: Couldn't allocate a recieve packet\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* compute the packet length */
|
/* compute the packet length */
|
||||||
|
|
||||||
if ( ( length < ( ETHER_BUFFER_SIZE - ( 2+2+2 ) ) ) )
|
if ( ( length < ( ETHER_BUFFER_SIZE - ( 2+2+2 ) ) ) )
|
||||||
{
|
{
|
||||||
uint8_t *const packet = &m_rx[ ( rx_fifo_in & ( ETHER_RX_BUFFERS - 1 ) ) * ETHER_BUFFER_SIZE];
|
uint8_t *const packet = &m_buffer[ packet_num * ETHER_BUFFER_SIZE];
|
||||||
|
|
||||||
std::fill_n(packet, ETHER_BUFFER_SIZE, 0);
|
|
||||||
|
|
||||||
int dst = 0;
|
int dst = 0;
|
||||||
|
|
||||||
@ -384,17 +418,15 @@ void smc91c9x_device::recv_cb(uint8_t *data, int length)
|
|||||||
packet[dst++] = 0x40 | 0x00; // Control
|
packet[dst++] = 0x40 | 0x00; // Control
|
||||||
}
|
}
|
||||||
|
|
||||||
dst += 2;
|
//dst += 2;
|
||||||
|
|
||||||
dst &= 0x7FF;
|
dst &= 0x7FF;
|
||||||
|
|
||||||
packet[2] = (dst&0xFF);
|
packet[2] = (dst&0xFF);
|
||||||
packet[3] = (dst) >> 8;
|
packet[3] = (dst) >> 8;
|
||||||
|
|
||||||
m_reg[EREG_INTERRUPT] |= EINT_RCV;
|
// Push packet number to rx completion fifo
|
||||||
m_reg[EREG_FIFO_PORTS] &= ~0x8000;
|
m_comp_rx.push(packet_num);
|
||||||
|
|
||||||
rx_fifo_in = (rx_fifo_in + 1) & ( ETHER_RX_BUFFERS - 1 );
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -416,12 +448,31 @@ void smc91c9x_device::recv_cb(uint8_t *data, int length)
|
|||||||
|
|
||||||
void smc91c9x_device::update_ethernet_irq()
|
void smc91c9x_device::update_ethernet_irq()
|
||||||
{
|
{
|
||||||
|
// Check tx completion fifo empty
|
||||||
|
if (m_comp_tx.empty()) {
|
||||||
|
m_reg[EREG_INTERRUPT] |= EINT_TX_EMPTY;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_reg[EREG_INTERRUPT] &= ~EINT_TX_EMPTY;
|
||||||
|
}
|
||||||
|
// Check rx completion fifo empty
|
||||||
|
if (m_comp_rx.empty())
|
||||||
|
m_reg[EREG_INTERRUPT] &= ~EINT_RCV;
|
||||||
|
else
|
||||||
|
m_reg[EREG_INTERRUPT] |= EINT_RCV;
|
||||||
|
|
||||||
uint8_t const mask = m_reg[EREG_INTERRUPT] >> 8;
|
uint8_t const mask = m_reg[EREG_INTERRUPT] >> 8;
|
||||||
uint8_t const state = m_reg[EREG_INTERRUPT] & 0xff;
|
uint8_t const state = m_reg[EREG_INTERRUPT] & 0xff;
|
||||||
|
|
||||||
|
|
||||||
/* update the IRQ state */
|
/* update the IRQ state */
|
||||||
m_irq_state = ((mask & state) != 0);
|
uint8_t new_state = mask & state;
|
||||||
m_irq_handler(m_irq_state ? ASSERT_LINE : CLEAR_LINE);
|
if (m_irq_state ^ new_state)
|
||||||
|
{
|
||||||
|
logerror("update_ethernet_irq: old: %02x new: %02x\n", m_irq_state, new_state);
|
||||||
|
m_irq_state = new_state;
|
||||||
|
m_irq_handler(m_irq_state ? ASSERT_LINE : CLEAR_LINE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -440,29 +491,19 @@ void smc91c9x_device::update_stats()
|
|||||||
send_frame - push a frame to the interface
|
send_frame - push a frame to the interface
|
||||||
-------------------------------------------------*/
|
-------------------------------------------------*/
|
||||||
|
|
||||||
int smc91c9x_device::send_frame()
|
TIMER_CALLBACK_MEMBER(smc91c9x_device::send_frame)
|
||||||
{
|
{
|
||||||
bool const is_broadcast = (m_tx[4] == 0xff && m_tx[5] == 0xff && m_tx[6] == 0xff &&
|
const int packet_num = m_comp_tx.front();
|
||||||
m_tx[7] == 0xff && m_tx[8] == 0xff && m_tx[9] == 0xff);
|
uint8_t *const tx_buffer = &m_buffer[packet_num * ETHER_BUFFER_SIZE];
|
||||||
|
|
||||||
tx_fifo_in = ( tx_fifo_in + 1 ) & ( ETHER_TX_BUFFERS - 1 );
|
/* update the EPH register */
|
||||||
|
|
||||||
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;
|
m_reg[EREG_EPH_STATUS] = 0x0001;
|
||||||
|
|
||||||
if (is_broadcast)
|
if (is_broadcast(&tx_buffer[4]))
|
||||||
m_reg[EREG_EPH_STATUS] |= 0x0040;
|
m_reg[EREG_EPH_STATUS] |= 0x0040;
|
||||||
|
|
||||||
tx_buffer[0] = m_reg[EREG_EPH_STATUS];
|
// signal a transmit interrupt
|
||||||
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;
|
||||||
m_reg[EREG_INTERRUPT] |= EINT_TX_EMPTY;
|
|
||||||
m_reg[EREG_FIFO_PORTS] |= 0x0080;
|
|
||||||
m_sent++;
|
m_sent++;
|
||||||
|
|
||||||
update_stats();
|
update_stats();
|
||||||
@ -483,31 +524,63 @@ int smc91c9x_device::send_frame()
|
|||||||
logerror("--- %d/0x%x bytes\n", buffer_len, buffer_len);
|
logerror("--- %d/0x%x bytes\n", buffer_len, buffer_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( buffer_len > 4 )
|
if (buffer_len > 4)
|
||||||
{
|
{
|
||||||
// odd or even sized frame ?
|
if (m_link_unconnected)
|
||||||
if (tx_buffer[buffer_len-1] & 0x20)
|
|
||||||
buffer_len--;
|
|
||||||
else
|
|
||||||
buffer_len -= 2;
|
|
||||||
|
|
||||||
if (!(m_reg[EREG_TCR] & 0x2002))
|
|
||||||
{
|
{
|
||||||
// No loopback... Send the frame
|
// Set lost carrier
|
||||||
if ( !send(&tx_buffer[4], buffer_len-4) )
|
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
|
||||||
|
{
|
||||||
|
// odd or even sized frame ?
|
||||||
|
if (tx_buffer[buffer_len - 1] & 0x20)
|
||||||
|
buffer_len--;
|
||||||
|
else
|
||||||
|
buffer_len -= 2;
|
||||||
|
|
||||||
|
// Send the frame
|
||||||
|
if (!send(&tx_buffer[4], buffer_len - 4))
|
||||||
{
|
{
|
||||||
// FIXME: failed to send the Ethernet packet
|
// FIXME: failed to send the Ethernet packet
|
||||||
//logerror("failed to send Ethernet packet\n");
|
//logerror("failed to send Ethernet packet\n");
|
||||||
//LOG(this,("read_command_port(): !!! failed to send Ethernet packet"));
|
//LOG(this,("read_command_port(): !!! failed to send Ethernet packet"));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
// Loopback if loopback is set or fduplx is set
|
||||||
{
|
// TODO: Figure out correct size
|
||||||
// TODO loopback mode : Push the frame to the RX FIFO.
|
// TODO: Check for addtional filter options for FDUPLX mode
|
||||||
|
if ((m_reg[EREG_TCR] & 0x2002) || (m_reg[EREG_TCR] & 0x0800))
|
||||||
|
recv_cb(&tx_buffer[4], buffer_len - 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Update status in the transmit word
|
||||||
|
tx_buffer[0] = m_reg[EREG_EPH_STATUS];
|
||||||
|
tx_buffer[1] = m_reg[EREG_EPH_STATUS] >> 8;
|
||||||
|
|
||||||
return 0;
|
update_ethernet_irq();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------
|
/*-------------------------------------------------
|
||||||
@ -524,10 +597,20 @@ void smc91c9x_device::process_command(uint16_t data)
|
|||||||
|
|
||||||
case ECMD_ALLOCATE:
|
case ECMD_ALLOCATE:
|
||||||
LOG(" 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++ & 0x7F) << 8;
|
int packet_num;
|
||||||
m_reg[EREG_INTERRUPT] |= 0x0008;
|
if (alloc_req(1, packet_num)) {
|
||||||
update_ethernet_irq();
|
// Set ARR register
|
||||||
|
m_reg[EREG_PNR_ARR] &= ~0xff00;
|
||||||
|
m_reg[EREG_PNR_ARR] |= packet_num << 8;
|
||||||
|
m_reg[EREG_INTERRUPT] |= EINT_ALLOC;
|
||||||
|
|
||||||
|
update_ethernet_irq();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logerror("ECMD_ALLOCATE: Couldn't allocate TX memory\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ECMD_RESET_MMU:
|
case ECMD_RESET_MMU:
|
||||||
@ -539,33 +622,24 @@ void smc91c9x_device::process_command(uint16_t data)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
LOG(" RESET MMU\n");
|
LOG(" RESET MMU\n");
|
||||||
// Flush fifos.
|
mmu_reset();
|
||||||
clear_tx_fifo();
|
|
||||||
clear_rx_fifo();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ECMD_REMOVE_TOPFRAME_TX:
|
case ECMD_REMOVE_TOPFRAME_TX:
|
||||||
LOG(" REMOVE FRAME FROM TX FIFO\n");
|
LOG(" REMOVE FRAME FROM TX FIFO\n");
|
||||||
|
m_comp_tx.pop();
|
||||||
|
// TODO: Should we clear TX_INT?
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ECMD_REMOVE_RELEASE_TOPFRAME_RX:
|
||||||
|
LOG(" REMOVE AND RELEASE FRAME FROM RX FIFO (PACK_NUM=%d)\n", m_comp_rx.front());
|
||||||
|
// Release memory allocation
|
||||||
|
alloc_release(m_comp_rx.front());
|
||||||
|
// Fall through
|
||||||
case ECMD_REMOVE_TOPFRAME_RX:
|
case ECMD_REMOVE_TOPFRAME_RX:
|
||||||
LOG(" REMOVE FRAME FROM RX FIFO\n");
|
LOG(" REMOVE FRAME FROM RX FIFO\n");
|
||||||
|
// remove entry from rx queue
|
||||||
case ECMD_REMOVE_RELEASE_TOPFRAME_RX:
|
m_comp_rx.pop();
|
||||||
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 ( (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 ) ) )
|
|
||||||
{
|
|
||||||
m_reg[EREG_INTERRUPT] |= EINT_RCV;
|
|
||||||
m_reg[EREG_FIFO_PORTS] &= ~0x8000;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_reg[EREG_FIFO_PORTS] |= 0x8000;
|
|
||||||
|
|
||||||
update_ethernet_irq();
|
update_ethernet_irq();
|
||||||
m_recd++;
|
m_recd++;
|
||||||
@ -573,49 +647,23 @@ void smc91c9x_device::process_command(uint16_t data)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ECMD_RELEASE_PACKET:
|
case ECMD_RELEASE_PACKET:
|
||||||
LOG(" RELEASE SPECIFIC PACKET\n");
|
{
|
||||||
|
const int packet_number = m_reg[EREG_PNR_ARR] & 0xff;
|
||||||
|
alloc_release(packet_number);
|
||||||
|
LOG(" RELEASE SPECIFIC PACKET %d\n", packet_number);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ECMD_ENQUEUE_PACKET:
|
case ECMD_ENQUEUE_PACKET:
|
||||||
LOG(" ENQUEUE TX PACKET\n");
|
LOG(" ENQUEUE TX PACKET\n");
|
||||||
|
|
||||||
if ( m_link_unconnected )
|
if (m_reg[EREG_TCR] & 0x0001) // TX EN ?
|
||||||
{
|
{
|
||||||
// Set lost carrier
|
const int packet_number = m_reg[EREG_PNR_ARR] & 0xff;
|
||||||
if ( m_reg[EREG_TCR] & 0x0400 )
|
// Push packet number tx completion fifo
|
||||||
{
|
m_comp_tx.push(packet_number);
|
||||||
m_reg[EREG_EPH_STATUS] |= 0x400;
|
m_tx_timer->adjust(attotime::from_usec(10));
|
||||||
// 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;
|
break;
|
||||||
|
|
||||||
case ECMD_RESET_FIFOS:
|
case ECMD_RESET_FIFOS:
|
||||||
@ -666,6 +714,19 @@ READ16_MEMBER( smc91c9x_device::read )
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EREG_FIFO_PORTS:
|
||||||
|
result = 0;
|
||||||
|
if (!m_comp_tx.empty())
|
||||||
|
result |= m_comp_tx.front();
|
||||||
|
else
|
||||||
|
result |= 0x80;
|
||||||
|
if (!m_comp_rx.empty())
|
||||||
|
result |= m_comp_rx.front() << 8;
|
||||||
|
else
|
||||||
|
result |= 0x80 << 8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
case EREG_DATA_0: /* data register */
|
case EREG_DATA_0: /* data register */
|
||||||
case EREG_DATA_1: /* data register */
|
case EREG_DATA_1: /* data register */
|
||||||
{
|
{
|
||||||
@ -673,13 +734,9 @@ READ16_MEMBER( smc91c9x_device::read )
|
|||||||
int addr = m_reg[EREG_POINTER] & 0x7ff;
|
int addr = m_reg[EREG_POINTER] & 0x7ff;
|
||||||
|
|
||||||
if ( m_reg[EREG_POINTER] & 0x8000 )
|
if ( m_reg[EREG_POINTER] & 0x8000 )
|
||||||
{
|
buffer = &m_buffer[m_comp_rx.front() * ETHER_BUFFER_SIZE];
|
||||||
buffer = &m_rx[(rx_fifo_out & ( ETHER_RX_BUFFERS - 1 )) * ETHER_BUFFER_SIZE];
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
buffer = &m_buffer[(m_reg[EREG_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];;
|
||||||
buffer = (uint8_t *)&m_tx[(tx_fifo_in & (ETHER_TX_BUFFERS-1))* ETHER_BUFFER_SIZE];;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = buffer[addr++];
|
result = buffer[addr++];
|
||||||
if ( ACCESSING_BITS_8_15 )
|
if ( ACCESSING_BITS_8_15 )
|
||||||
@ -799,15 +856,12 @@ WRITE16_MEMBER( smc91c9x_device::write )
|
|||||||
uint8_t *buffer;
|
uint8_t *buffer;
|
||||||
int addr = m_reg[EREG_POINTER] & 0x7ff;
|
int addr = m_reg[EREG_POINTER] & 0x7ff;
|
||||||
|
|
||||||
if ( m_reg[EREG_POINTER] & 0x8000 )
|
if (m_reg[EREG_POINTER] & 0x8000)
|
||||||
{
|
buffer = &m_buffer[m_comp_rx.front() * ETHER_BUFFER_SIZE];
|
||||||
buffer = &m_rx[(rx_fifo_out & ( ETHER_RX_BUFFERS - 1 )) * ETHER_BUFFER_SIZE];
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
buffer = &m_buffer[(m_reg[EREG_PNR_ARR] & 0x1f) * ETHER_BUFFER_SIZE];;
|
||||||
buffer = (uint8_t *)&m_tx[(tx_fifo_in & (ETHER_TX_BUFFERS-1))* ETHER_BUFFER_SIZE];;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO: Should be checking if incr is set
|
||||||
buffer[addr++] = data;
|
buffer[addr++] = data;
|
||||||
if ( ACCESSING_BITS_8_15 )
|
if ( ACCESSING_BITS_8_15 )
|
||||||
buffer[addr++] = data >> 8;
|
buffer[addr++] = data >> 8;
|
||||||
@ -817,10 +871,12 @@ WRITE16_MEMBER( smc91c9x_device::write )
|
|||||||
}
|
}
|
||||||
|
|
||||||
case EREG_INTERRUPT:
|
case EREG_INTERRUPT:
|
||||||
m_reg[EREG_INTERRUPT] &= ~(data & 0x56);
|
// Pop tx fifo packet from completion fifo if clear tx int is set
|
||||||
// Need to clear tx int here for vegas cartfury
|
if (m_reg[EREG_INTERRUPT] & data & EINT_TX) {
|
||||||
if ( m_reg[EREG_FIFO_PORTS] & 0x0080 )
|
m_comp_tx.pop();
|
||||||
m_reg[EREG_INTERRUPT] &= ~EINT_TX;
|
m_reg[EREG_INTERRUPT] &= ~EINT_TX;
|
||||||
|
}
|
||||||
|
m_reg[EREG_INTERRUPT] &= ~(data & 0x56);
|
||||||
update_ethernet_irq();
|
update_ethernet_irq();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#ifndef MAME_MACHINE_SMC91C9X_H
|
#ifndef MAME_MACHINE_SMC91C9X_H
|
||||||
#define MAME_MACHINE_SMC91C9X_H
|
#define MAME_MACHINE_SMC91C9X_H
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
TYPE DEFINITIONS
|
TYPE DEFINITIONS
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
@ -22,8 +24,10 @@ public:
|
|||||||
|
|
||||||
DECLARE_READ16_MEMBER( read );
|
DECLARE_READ16_MEMBER( read );
|
||||||
DECLARE_WRITE16_MEMBER( write );
|
DECLARE_WRITE16_MEMBER( write );
|
||||||
|
TIMER_CALLBACK_MEMBER(send_frame);
|
||||||
|
|
||||||
virtual void recv_cb(uint8_t *data, int length) override;
|
virtual void recv_cb(uint8_t *data, int length) override;
|
||||||
|
void set_link_connected(bool connected) { m_link_unconnected = !connected; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
smc91c9x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
|
smc91c9x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
|
||||||
@ -34,10 +38,22 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr unsigned ETHER_BUFFER_SIZE = 2048;
|
static constexpr unsigned ETHER_BUFFER_SIZE = 2048;
|
||||||
static constexpr unsigned ETHER_RX_BUFFERS = 16;
|
// TODO: 96 device is larger
|
||||||
static constexpr unsigned ETHER_TX_BUFFERS = 16;
|
static constexpr unsigned ETHER_BUFFERS = 16;
|
||||||
static constexpr unsigned ETHERNET_ADDR_SIZE = 6;
|
static constexpr unsigned ETHERNET_ADDR_SIZE = 6;
|
||||||
|
|
||||||
|
// mmu
|
||||||
|
// The bits in these vectors indicate a packet has been allocated
|
||||||
|
u32 m_alloc_rx, m_alloc_tx;
|
||||||
|
std::queue<int> m_comp_tx, m_comp_rx;
|
||||||
|
// Requests a packet allocation and returns true
|
||||||
|
// and sets the packet number if successful
|
||||||
|
bool alloc_req(const int tx, int &packet_num);
|
||||||
|
// Releases an allocation
|
||||||
|
void alloc_release(const int packet_num);
|
||||||
|
// Resets the MMU
|
||||||
|
void mmu_reset();
|
||||||
|
|
||||||
// internal state
|
// internal state
|
||||||
devcb_write_line m_irq_handler;
|
devcb_write_line m_irq_handler;
|
||||||
|
|
||||||
@ -51,22 +67,15 @@ private:
|
|||||||
/* IRQ information */
|
/* IRQ information */
|
||||||
uint8_t m_irq_state;
|
uint8_t m_irq_state;
|
||||||
|
|
||||||
/* allocate information */
|
// Main memory
|
||||||
uint8_t m_alloc_count;
|
uint8_t m_buffer[ETHER_BUFFER_SIZE * ETHER_BUFFERS];
|
||||||
|
|
||||||
/* transmit/receive FIFOs */
|
|
||||||
uint32_t rx_fifo_out;
|
|
||||||
uint32_t rx_fifo_in;
|
|
||||||
uint8_t m_rx[ETHER_BUFFER_SIZE * ETHER_RX_BUFFERS];
|
|
||||||
|
|
||||||
uint32_t tx_fifo_out;
|
|
||||||
uint32_t tx_fifo_in;
|
|
||||||
uint8_t m_tx[ETHER_BUFFER_SIZE * ETHER_TX_BUFFERS];
|
|
||||||
|
|
||||||
/* counters */
|
/* counters */
|
||||||
uint32_t m_sent;
|
uint32_t m_sent;
|
||||||
uint32_t m_recd;
|
uint32_t m_recd;
|
||||||
|
|
||||||
|
emu_timer* m_tx_timer;
|
||||||
|
|
||||||
int ethernet_packet_is_for_me(const uint8_t mac_address[]);
|
int ethernet_packet_is_for_me(const uint8_t mac_address[]);
|
||||||
int is_broadcast(uint8_t mac_address[]);
|
int is_broadcast(uint8_t mac_address[]);
|
||||||
|
|
||||||
@ -77,8 +86,6 @@ private:
|
|||||||
void clear_tx_fifo();
|
void clear_tx_fifo();
|
||||||
void clear_rx_fifo();
|
void clear_rx_fifo();
|
||||||
|
|
||||||
int send_frame();
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user