summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/isa/3c505.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/isa/3c505.c')
-rw-r--r--src/devices/bus/isa/3c505.c1667
1 files changed, 1667 insertions, 0 deletions
diff --git a/src/devices/bus/isa/3c505.c b/src/devices/bus/isa/3c505.c
new file mode 100644
index 00000000000..98328c41683
--- /dev/null
+++ b/src/devices/bus/isa/3c505.c
@@ -0,0 +1,1667 @@
+// license:BSD-3-Clause
+// copyright-holders:Hans Ostermeyer,R. Belmont
+/*
+ * 3c505.c - 3COM 3C505 ethernet controller (for Apollo DN3x00)
+ *
+ * Created on: August 27, 2010
+ * Author: Hans Ostermeyer
+ * ISA conversion by R. Belmont
+ *
+ * see also:
+ * - http://lxr.free-electrons.com/source/drivers/net/3c505.h
+ * - http://lxr.free-electrons.com/source/drivers/net/3c505.c
+ * - http://stason.org/TULARC/pc/network-cards/O/OLIVETTI-Ethernet-NPU-9144-3C505.html
+ * - http://www.bitsavers.org/pdf/3Com/3c505_Etherlink_Plus_Developers_Guide_May86.pdf'
+ * - http://www.bitsavers.org/pdf/3Com/1569-03_EtherLink_Plus_Technical_Reference_Jan89.pdf
+ *
+ */
+
+#include "3c505.h"
+
+#define VERBOSE 0
+
+static int verbose = VERBOSE;
+
+#define LOG(x) { logerror ("%s: ", cpu_context()); logerror x; logerror ("\n"); }
+#define LOG1(x) { if (verbose > 0) LOG(x)}
+#define LOG2(x) { if (verbose > 1) LOG(x)}
+
+#define MAINCPU "maincpu"
+
+#ifdef LSB_FIRST
+static UINT16 uint16_to_le(UINT16 value)
+{
+ return value;
+}
+
+static UINT16 uint16_from_le(UINT16 value)
+{
+ return value;
+}
+#else
+static UINT16 uint16_to_le(UINT16 value)
+{
+ return ((value&0x00ff)<<8)|((value&0xff00)>>8);
+}
+
+static UINT16 uint16_from_le(UINT16 value)
+{
+ return ((value&0x00ff)<<8)|((value&0xff00)>>8);
+}
+#endif
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+// Apollo microcode version
+#define APOLLO_MC_VERSION_SR10_2 0x0302
+#define APOLLO_MC_VERSION_SR10_4 0x0303
+
+#define PORT_DATA_FIFO_SIZE 20
+
+/*
+ * I/O register offsets
+ */
+#define PORT_COMMAND 0x00 /* read/write, 8-bit */
+#define PORT_STATUS 0x02 /* read only, 8-bit */
+#define PORT_AUXDMA 0x02 /* write only, 8-bit */
+#define PORT_DATA 0x04 /* read/write, 16-bit */
+#define PORT_CONTROL 0x06 /* read/write, 8-bit */
+
+#define ELP_IO_EXTENT 0x10 /* size of used IO registers */
+
+/*
+ * host control registers bits
+ */
+#define ATTN 0x80 /* attention */
+#define FLSH 0x40 /* flush data register */
+#define DMAE 0x20 /* DMA enable */
+#define DIR_ 0x10 /* direction */
+#define TCEN 0x08 /* terminal count interrupt enable */
+#define CMDE 0x04 /* command register interrupt enable */
+#define HSF2 0x02 /* host status flag 2 */
+#define HSF1 0x01 /* host status flag 1 */
+
+/*
+ * combinations of HSF flags used for PCB transmission
+ */
+#define HSF_PCB_ACK HSF1
+#define HSF_PCB_NAK HSF2
+#define HSF_PCB_END (HSF2|HSF1)
+#define HSF_PCB_MASK (HSF2|HSF1)
+
+/*
+ * host status register bits
+ */
+#define HRDY 0x80 /* data register ready */
+#define HCRE 0x40 /* command register empty */
+#define ACRF 0x20 /* adapter command register full */
+/* #define DIR_ 0x10 direction - same as in control register */
+#define DONE 0x08 /* DMA done */
+#define ASF3 0x04 /* adapter status flag 3 */
+#define ASF2 0x02 /* adapter status flag 2 */
+#define ASF1 0x01 /* adapter status flag 1 */
+
+/*
+ * combinations of ASF flags used for PCB reception
+ */
+#define ASF_PCB_ACK ASF1
+#define ASF_PCB_NAK ASF2
+#define ASF_PCB_END (ASF2|ASF1)
+#define ASF_PCB_MASK (ASF3|ASF2|ASF1)
+
+/*
+ * host aux DMA register bits
+ */
+#define DMA_BRST 0x01 /* DMA burst */
+
+/*
+ * maximum amount of data allowed in a PCB
+ */
+#define MAX_PCB_DATA 62
+
+#define CMD_RESPONSE_OFFSET 0x30
+enum
+{
+ /* host PCB commands */
+ CMD_RESET = 0x00,
+ CMD_CONFIGURE_ADAPTER_MEMORY = 0x01,
+ CMD_CONFIGURE_82586 = 0x02,
+ CMD_STATION_ADDRESS = 0x03,
+ CMD_DMA_DOWNLOAD = 0x04,
+ CMD_DMA_UPLOAD = 0x05,
+ CMD_PIO_DOWNLOAD = 0x06,
+ CMD_PIO_UPLOAD = 0x07,
+ CMD_RECEIVE_PACKET = 0x08,
+ CMD_TRANSMIT_PACKET = 0x09,
+ CMD_NETWORK_STATISTICS = 0x0a,
+ CMD_LOAD_MULTICAST_LIST = 0x0b,
+ CMD_CLEAR_PROGRAM = 0x0c,
+ CMD_DOWNLOAD_PROGRAM = 0x0d,
+ CMD_EXECUTE_PROGRAM = 0x0e,
+ CMD_SELF_TEST = 0x0f,
+ CMD_SET_STATION_ADDRESS = 0x10,
+ CMD_ADAPTER_INFO = 0x11,
+
+ CMD_MC_17 = 0x17,
+ CMD_TRANSMIT_PACKET_18 = 0x18,
+ CMD_MC_F8 = 0xf8,
+ CMD_TRANSMIT_PACKET_F9 = 0xf9,
+ CMD_MC_FA = 0xfa,
+
+ /*adapter PCB commands */
+ CMD_RESET_RESPONSE = 0x30,
+ CMD_CONFIGURE_ADAPTER_RESPONSE = 0x31,
+ CMD_CONFIGURE_82586_RESPONSE = 0x32,
+ CMD_ADDRESS_RESPONSE = 0x33,
+ CMD_DOWNLOAD_DATA_REQUEST = 0x34,
+ CMD_UPLOAD_DATA_REQUEST = 0x35,
+ CMD_RECEIVE_PACKET_COMPLETE = 0x38,
+ CMD_TRANSMIT_PACKET_COMPLETE = 0x39,
+ CMD_NETWORK_STATISTICS_RESPONSE = 0x3a,
+ CMD_LOAD_MULTICAST_RESPONSE = 0x3b,
+ CMD_CLEAR_PROGRAM_RESPONSE = 0x3c,
+ CMD_DOWNLOAD_PROGRAM_RESPONSE = 0x3d,
+ CMD_EXECUTE_PROGRAM_RESPONSE = 0x3e,
+ CMD_SELF_TEST_RESPONSE = 0x3f,
+ CMD_SET_ADDRESS_RESPONSE = 0x40,
+ CMD_ADAPTER_INFO_RESPONSE = 0x41,
+
+ CMD_MC_17_COMPLETE = 0x47,
+ CMD_TRANSMIT_PACKET_18_COMPLETE = 0x48,
+
+ CMD_MC_E1_RESPONSE = 0xe1,
+ CMD_MC_E2_RESPONSE = 0xe2
+};
+
+/* defines for 'configure' */
+
+#define RECV_STATION 0x00
+#define RECV_BROAD 0x01
+#define RECV_MULTI 0x02
+#define RECV_PROMISC 0x04
+#define NO_LOOPBACK 0x00
+#define INT_LOOPBACK 0x08
+#define EXT_LOOPBACK 0x10
+
+ROM_START( threecom3c505 )
+ ROM_REGION( 0x02000, "threecom3c505", 0 )
+ ROM_LOAD_OPTIONAL( "3000_3c505_010728-00.bin", 0x00000, 0x02000, CRC(69b77ec6) SHA1(7ac36cc6fc90b90ddfc56c45303b514cbe18ae58) )
+ // see http://www.bitsavers.org/bits/Apollo/firmware/
+ROM_END
+
+static INPUT_PORTS_START( tc3c505_port )
+ PORT_START("IO_BASE")
+ PORT_DIPNAME( 0x3f0, 0x300, "3C505 I/O base")
+ PORT_DIPSETTING( 0x010, "010h" )
+ PORT_DIPSETTING( 0x020, "020h" )
+ PORT_DIPSETTING( 0x030, "030h" )
+ PORT_DIPSETTING( 0x040, "040h" )
+ PORT_DIPSETTING( 0x050, "050h" )
+ PORT_DIPSETTING( 0x060, "060h" )
+ PORT_DIPSETTING( 0x070, "070h" )
+ PORT_DIPSETTING( 0x080, "080h" )
+ PORT_DIPSETTING( 0x090, "090h" )
+ PORT_DIPSETTING( 0x0a0, "0a0h" )
+ PORT_DIPSETTING( 0x0b0, "0b0h" )
+ PORT_DIPSETTING( 0x0c0, "0c0h" )
+ PORT_DIPSETTING( 0x0d0, "0d0h" )
+ PORT_DIPSETTING( 0x0e0, "0e0h" )
+ PORT_DIPSETTING( 0x0f0, "0f0h" )
+ PORT_DIPSETTING( 0x100, "0100h" )
+ PORT_DIPSETTING( 0x110, "0110h" )
+ PORT_DIPSETTING( 0x120, "0120h" )
+ PORT_DIPSETTING( 0x130, "0130h" )
+ PORT_DIPSETTING( 0x140, "0140h" )
+ PORT_DIPSETTING( 0x150, "0150h" )
+ PORT_DIPSETTING( 0x160, "0160h" )
+ PORT_DIPSETTING( 0x170, "0170h" )
+ PORT_DIPSETTING( 0x180, "0180h" )
+ PORT_DIPSETTING( 0x190, "0190h" )
+ PORT_DIPSETTING( 0x1a0, "01a0h" )
+ PORT_DIPSETTING( 0x1b0, "01b0h" )
+ PORT_DIPSETTING( 0x1c0, "01c0h" )
+ PORT_DIPSETTING( 0x1d0, "01d0h" )
+ PORT_DIPSETTING( 0x1e0, "01e0h" )
+ PORT_DIPSETTING( 0x1f0, "01f0h" )
+ PORT_DIPSETTING( 0x200, "0200h" )
+ PORT_DIPSETTING( 0x210, "0210h" )
+ PORT_DIPSETTING( 0x220, "0220h" )
+ PORT_DIPSETTING( 0x230, "0230h" )
+ PORT_DIPSETTING( 0x240, "0240h" )
+ PORT_DIPSETTING( 0x250, "0250h" )
+ PORT_DIPSETTING( 0x260, "0260h" )
+ PORT_DIPSETTING( 0x270, "0270h" )
+ PORT_DIPSETTING( 0x280, "0280h" )
+ PORT_DIPSETTING( 0x290, "0290h" )
+ PORT_DIPSETTING( 0x2a0, "02a0h" )
+ PORT_DIPSETTING( 0x2b0, "02b0h" )
+ PORT_DIPSETTING( 0x2c0, "02c0h" )
+ PORT_DIPSETTING( 0x2d0, "02d0h" )
+ PORT_DIPSETTING( 0x2e0, "02e0h" )
+ PORT_DIPSETTING( 0x2f0, "02f0h" )
+ PORT_DIPSETTING( 0x300, "0300h" )
+ PORT_DIPSETTING( 0x310, "0310h" )
+ PORT_DIPSETTING( 0x320, "0320h" )
+ PORT_DIPSETTING( 0x330, "0330h" )
+ PORT_DIPSETTING( 0x340, "0340h" )
+ PORT_DIPSETTING( 0x350, "0350h" )
+ PORT_DIPSETTING( 0x360, "0360h" )
+ PORT_DIPSETTING( 0x370, "0370h" )
+ PORT_DIPSETTING( 0x380, "0380h" )
+ PORT_DIPSETTING( 0x390, "0390h" )
+ PORT_DIPSETTING( 0x3a0, "03a0h" )
+ PORT_DIPSETTING( 0x3b0, "03b0h" )
+ PORT_DIPSETTING( 0x3c0, "03c0h" )
+ PORT_DIPSETTING( 0x3d0, "03d0h" )
+ PORT_DIPSETTING( 0x3e0, "03e0h" )
+ PORT_DIPSETTING( 0x3f0, "03f0h" )
+
+ PORT_START("IRQ_DRQ")
+ PORT_DIPNAME( 0x0f, 0x0a, "3C505 IRQ")
+ PORT_DIPSETTING( 0x03, "IRQ 3" )
+ PORT_DIPSETTING( 0x04, "IRQ 4" )
+ PORT_DIPSETTING( 0x05, "IRQ 5" )
+ PORT_DIPSETTING( 0x06, "IRQ 6" )
+ PORT_DIPSETTING( 0x07, "IRQ 7" )
+ PORT_DIPSETTING( 0x09, "IRQ 9" )
+ PORT_DIPSETTING( 0x0a, "IRQ 10" )
+ PORT_DIPSETTING( 0x0b, "IRQ 11" )
+ PORT_DIPSETTING( 0x0c, "IRQ 12" )
+ PORT_DIPSETTING( 0x0e, "IRQ 14" )
+ PORT_DIPSETTING( 0x0f, "IRQ 15" )
+
+ PORT_DIPNAME( 0x70, 0x60, "3C505 DMA")
+ PORT_DIPSETTING( 0x00, "none" )
+ PORT_DIPSETTING( 0x10, "DRQ 1" )
+ PORT_DIPSETTING( 0x30, "DRQ 3" )
+ PORT_DIPSETTING( 0x50, "DRQ 5" )
+ PORT_DIPSETTING( 0x60, "DRQ 6" )
+ PORT_DIPSETTING( 0x70, "DRQ 7" )
+
+ PORT_START("ROM_OPTS")
+ PORT_DIPNAME( 0x01, 0x01, "ROM control")
+ PORT_DIPSETTING( 0x00, "Disabled" )
+ PORT_DIPSETTING( 0x01, "Enabled" )
+ PORT_DIPNAME( 0x06, 0x00, "ROM base")
+ PORT_DIPSETTING( 0x00, "80000h" )
+ PORT_DIPSETTING( 0x02, "82000h" )
+ PORT_DIPSETTING( 0x04, "84000h" )
+ PORT_DIPSETTING( 0x06, "86000h" )
+
+INPUT_PORTS_END
+
+/***************************************************************************
+ IMPLEMENTATION
+ ***************************************************************************/
+
+// device type definition
+const device_type ISA16_3C505 = &device_creator<threecom3c505_device> ;
+
+//-------------------------------------------------
+// threecom3c505_device - constructor
+//-------------------------------------------------
+
+threecom3c505_device::threecom3c505_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, ISA16_3C505, "3Com 3C505 Network Adaptor", tag, owner, clock, "3c505", __FILE__),
+ device_network_interface(mconfig, *this, 10.0f),
+ device_isa16_card_interface(mconfig, *this),
+ m_iobase(*this, "IO_BASE"),
+ m_irqdrq(*this, "IRQ_DRQ"),
+ m_romopts(*this, "ROM_OPTS")
+{
+}
+
+threecom3c505_device::threecom3c505_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, type, "3Com 3C505 Network Adaptor", tag, owner, clock, "3c505", __FILE__),
+ device_network_interface(mconfig, *this, 10.0f),
+ device_isa16_card_interface(mconfig, *this),
+ m_iobase(*this, "IO_BASE"),
+ m_irqdrq(*this, "IRQ_DRQ"),
+ m_romopts(*this, "ROM_OPTS")
+{
+}
+
+ioport_constructor threecom3c505_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME( tc3c505_port );
+}
+
+const rom_entry *threecom3c505_device::device_rom_region() const
+{
+ return ROM_NAME( threecom3c505 );
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void threecom3c505_device::device_start()
+{
+ set_isa_device();
+
+ LOG1(("start 3COM 3C505"));
+
+ m_installed = false;
+
+ m_rx_fifo.start(this, RX_FIFO_SIZE, ETH_BUFFER_SIZE);
+ m_rx_data_buffer.start(this, ETH_BUFFER_SIZE);
+ m_tx_data_buffer.start(this, ETH_BUFFER_SIZE);
+ m_program_buffer.start(this, PGM_BUFFER_SIZE);
+
+ m_do_command_timer = timer_alloc(0, NULL);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void threecom3c505_device::device_reset()
+{
+ LOG1(("reset 3COM 3C505"));
+
+ m_rx_fifo.reset();
+ m_rx_data_buffer.reset();
+ m_tx_data_buffer.reset();
+ m_program_buffer.reset();
+
+ memset(m_reg, 0, sizeof(m_reg));
+ m_status = HCRE | DIR_;
+ m_control = 0;
+ m_command_index = 0;
+ m_command_pending = 0;
+ m_wait_for_nak = 0;
+ m_wait_for_ack = 0;
+ m_rx_data_index = 0;
+ m_rx_pending = 0;
+ m_tx_data_length = 0;
+ m_response_length = 0;
+ m_response_index = 0;
+ m_microcode_running = 0;
+ m_microcode_version = 0;
+ m_i82586_config = 0;
+
+ // these will appear in /etc/nodestat -l
+ m_netstat.tot_recv = 0;
+ m_netstat.tot_xmit = 0;
+ m_netstat.err_CRC = 0;
+ m_netstat.err_align = 0;
+ m_netstat.err_res = 0;
+ m_netstat.err_ovrrun = 0;
+
+ memset(m_station_address, 0, sizeof(m_station_address));
+ memset(m_multicast_list, 0, sizeof(m_multicast_list));
+ set_filter_list();
+ set_promisc(true);
+
+ if (!m_installed)
+ {
+ int base = m_iobase->read();
+
+ m_irq = m_irqdrq->read() & 0xf;
+ m_drq = (m_irqdrq->read() >> 4) & 0x7;
+
+ m_isa->install16_device(base, base + ELP_IO_EXTENT - 1, 0, 0, read16_delegate(FUNC(threecom3c505_device::read), this), write16_delegate(FUNC(threecom3c505_device::write), this));
+
+ if (m_romopts->read() & 1)
+ {
+ // host ROM is enabled, get base address
+ static const int rom_bases[4] = { 0x0000, 0x2000, 0x4000, 0x6000 };
+ int rom_base = rom_bases[(m_romopts->read() >> 1) & 3];
+ m_isa->install_rom(this, rom_base, rom_base + 0x01fff, 0, 0, "threecom3c505", "threecom3c505");
+ }
+
+ m_installed = true;
+ }
+}
+
+/***************************************************************************
+ cpu_context - return a string describing the current CPU context
+ ***************************************************************************/
+
+const char *threecom3c505_device::cpu_context()
+{
+ static char statebuf[64]; /* string buffer containing state description */
+
+ device_t *cpu = machine().device(MAINCPU);
+ osd_ticks_t t = osd_ticks();
+ int s = t / osd_ticks_per_second();
+ int ms = (t % osd_ticks_per_second()) / 1000;
+
+ /* if we have an executing CPU, output data */
+ if (cpu != NULL)
+ {
+ sprintf(statebuf, "%d.%03d %s pc=%08x - %s", s, ms, cpu->tag(),
+ cpu->safe_pcbase(), tag());
+ }
+ else
+ {
+ sprintf(statebuf, "%d.%03d", s, ms);
+ }
+ return statebuf;
+}
+
+//**************************************************************************
+// data_buffer
+//**************************************************************************
+
+threecom3c505_device::data_buffer::data_buffer() :
+ m_device(NULL)
+{
+}
+
+void threecom3c505_device::data_buffer::start(threecom3c505_device *device, INT32 size)
+{
+ m_device = device;
+ LOG2(("start threecom3c505_device::data_buffer with size %0x", size));
+ m_data.resize(size);
+}
+
+void threecom3c505_device::data_buffer::reset()
+{
+ LOG2(("reset threecom3c505_device::data_buffer"));
+ m_length = 0;
+}
+
+void threecom3c505_device::data_buffer::copy(data_buffer *db) const
+{
+ db->m_data.resize(m_data.size());
+ db->m_length = m_length;
+ memcpy(&db->m_data[0], &m_data[0], m_data.size());
+}
+
+int threecom3c505_device::data_buffer::append(UINT8 data)
+{
+ if (m_length >= m_data.size())
+ {
+ return 0;
+ }
+ else
+ {
+ m_data[m_length++] = data;
+ return 1;
+ }
+}
+
+void threecom3c505_device::data_buffer::log(const char * title) const
+{
+ if (verbose > 0)
+ {
+ int i;
+ logerror("%s: %s (length=%02x)", m_device->cpu_context(), title, m_length);
+ for (i = 0; i < m_length; i++)
+ {
+ logerror(" %02x", m_data[i]);
+ if (i >= 1023)
+ {
+ logerror(" ...");
+ break;
+ }
+ }
+ logerror("\n");
+ }
+}
+
+//**************************************************************************
+// data_buffer fifo
+//**************************************************************************
+
+threecom3c505_device::data_buffer_fifo::data_buffer_fifo() :
+ m_device(NULL),
+ m_size(0)
+{
+}
+
+void threecom3c505_device::data_buffer_fifo::start(
+ threecom3c505_device *device, INT32 size, INT32 db_size)
+{
+ m_device = device;
+ LOG2(("start threecom3c505_device::data_buffer_fifo"));
+
+ int i;
+ // FIXME: fifo size is hardcoded
+ m_size = RX_FIFO_SIZE; // size;
+ for (i = 0; i < m_size; i++)
+ {
+ m_db[i] = global_alloc(data_buffer());
+ m_db[i]->start(device, db_size);
+ }
+}
+
+void threecom3c505_device::data_buffer_fifo::reset()
+{
+ LOG2(("reset threecom3c505_device::data_buffer_fifo"));
+ m_get_index = m_put_index = 0;
+ m_count = 0;
+}
+
+threecom3c505_device::data_buffer_fifo::~data_buffer_fifo()
+{
+ for (int i = 0; i < m_size; i++)
+ {
+ global_free(m_db[i]);
+ }
+}
+
+int threecom3c505_device::data_buffer_fifo::put(const UINT8 data[], const int length)
+{
+ UINT16 next_index = (m_put_index + 1) % m_size;
+
+ LOG2(("threecom3c505_device::data_buffer_fifo::put %d", m_count));
+
+ if (next_index == m_get_index)
+ {
+ // overflow, fifo full
+ return 0;
+ }
+ else if (length > ETH_BUFFER_SIZE)
+ {
+ // data size exceeds buffer size
+ LOG(("threecom3c505_device::data_buffer_fifo::put %d: data size (%d) exceeds buffer size (%d)!!!", m_count, length,ETH_BUFFER_SIZE));
+ return 0;
+ }
+ else
+ {
+ memcpy(&m_db[m_put_index]->m_data[0], data, length);
+ m_db[m_put_index]->m_length = length;
+ m_put_index = next_index;
+ m_count++;
+ return 1;
+ }
+}
+
+int threecom3c505_device::data_buffer_fifo::get(data_buffer *db)
+{
+ LOG2(("threecom3c505_device::data_buffer_fifo::get %d", m_count));
+
+ if (m_get_index == m_put_index)
+ {
+ // fifo empty
+ return 0;
+ }
+ else
+ {
+ m_db[m_get_index]->copy(db);
+ m_get_index = (m_get_index + 1) % m_size;
+ m_count--;
+ return 1;
+ }
+}
+
+/*-------------------------------------------------
+ set_filter_list - set the ethernet packet filter list
+ -------------------------------------------------*/
+
+void threecom3c505_device::set_filter_list()
+{
+ memset(m_filter_list, 0, sizeof(m_filter_list));
+ memcpy(m_filter_list, m_station_address, ETHERNET_ADDR_SIZE);
+ memset(m_filter_list + ETHERNET_ADDR_SIZE, 0xff, ETHERNET_ADDR_SIZE);
+ memcpy(m_filter_list + ETHERNET_ADDR_SIZE * 2, m_multicast_list, sizeof(m_multicast_list));
+
+ int node_id = (((m_station_address[3] << 8) + m_station_address[4]) << 8) + m_station_address[5];
+ LOG2(("set_filter_list node_id=%x",node_id));
+
+ setfilter(this, node_id);
+}
+
+/*-------------------------------------------------
+ set_interrupt - set the IRQ state
+ -------------------------------------------------*/
+
+void threecom3c505_device::set_interrupt(enum line_state state)
+{
+ if (state != irq_state)
+ {
+ LOG2(("set_interrupt(%d)",state));
+ switch (m_irq)
+ {
+ case 3: m_isa->irq3_w(state); break;
+ case 4: m_isa->irq4_w(state); break;
+ case 5: m_isa->irq5_w(state); break;
+ case 6: m_isa->irq6_w(state); break;
+ case 7: m_isa->irq7_w(state); break;
+ case 9: m_isa->irq2_w(state); break; // IRQ 9 on ISA16 goes to IRQ 2
+ case 10: m_isa->irq10_w(state); break;
+ case 11: m_isa->irq11_w(state); break;
+ case 12: m_isa->irq12_w(state); break;
+ case 14: m_isa->irq14_w(state); break;
+ case 15: m_isa->irq15_w(state); break;
+ default: logerror("3c505: invalid IRQ %d\n", m_irq); break;
+ }
+ irq_state = state;
+ }
+}
+
+// -------------------------------------
+
+void threecom3c505_device::log_command()
+{
+ if (verbose > 0)
+ {
+ int i;
+ logerror("%s: Command ", cpu_context());
+ switch (m_command_buffer[0])
+ {
+ case CMD_RESET: // 0x00
+ logerror("!!! unexpected CMD_RESET");
+ break;
+ case CMD_CONFIGURE_ADAPTER_MEMORY: // 0x01
+ logerror("CMD_CONFIGURE_ADAPTER_MEMORY");
+ break;
+ case CMD_CONFIGURE_82586: // 0x02
+ logerror("CMD_CONFIGURE_82586");
+ break;
+ case CMD_RECEIVE_PACKET: // 0x08
+ logerror("CMD_RECEIVE_PACKET");
+ break;
+ case CMD_TRANSMIT_PACKET: // 0x09
+ logerror("CMD_TRANSMIT_PACKET");
+ break;
+ case CMD_NETWORK_STATISTICS: // 0x0a
+ logerror("CMD_NETWORK_STATISTICS");
+ break;
+ case CMD_LOAD_MULTICAST_LIST: // 0x0b,
+ logerror("CMD_LOAD_MULTICAST_LIST");
+ break;
+ case CMD_CLEAR_PROGRAM: // 0x0c
+ logerror("!!! unexpected CMD_CLEAR_PROGRAM");
+ break;
+ case CMD_DOWNLOAD_PROGRAM: // 0x0d
+ logerror("CMD_DOWNLOAD_PROGRAM");
+ break;
+ case CMD_EXECUTE_PROGRAM: // 0x0e
+ logerror("CMD_EXECUTE_PROGRAM");
+ break;
+ case CMD_SET_STATION_ADDRESS: // 0x10
+ logerror("CMD_SET_STATION_ADDRESS");
+ break;
+ case CMD_ADAPTER_INFO: // 0x11
+ logerror("CMD_ADAPTER_INFO");
+ break;
+ case CMD_MC_17: // 0x17
+ logerror("CMD_MC_17");
+ break;
+ case CMD_TRANSMIT_PACKET_18: // 0x18
+ logerror("CMD_TRANSMIT_PACKET_18");
+ break;
+
+ case CMD_MC_F8: // 0xf8
+ logerror("!!! CMD_MC_F8");
+ break;
+ case CMD_TRANSMIT_PACKET_F9: // 0xf9
+ logerror("CMD_TRANSMIT_PACKET_F9");
+ break;
+ case CMD_MC_FA: // 0xfa
+ logerror("!!! CMD_MC_FA");
+ break;
+
+ default:
+ logerror("!!! unexpected Command");
+ }
+
+ switch (m_command_buffer[0])
+ {
+ case CMD_TRANSMIT_PACKET_F9: // 0xf9
+ logerror(" (%02x, length=00)", m_command_buffer[0]);
+ break;
+
+ default:
+ logerror(" (%02x, length=%02x)", m_command_buffer[0],
+ m_command_buffer[1]);
+ for (i = 2; i < m_command_index; i++)
+ {
+ logerror(" %02x", m_command_buffer[i]);
+ }
+ break;
+ }
+ logerror("\n");
+ }
+}
+
+void threecom3c505_device::log_response()
+{
+ if (verbose > 0)
+ {
+ int i;
+ logerror("%s: Response ", cpu_context());
+ switch (m_response.command)
+ {
+ case CMD_RESET_RESPONSE: // 0x30
+ logerror("CMD_RESET_RESPONSE");
+ break;
+ case CMD_CONFIGURE_ADAPTER_RESPONSE: // 0x31
+ logerror("CMD_CONFIGURE_ADAPTER_RESPONSE");
+ break;
+ case CMD_CONFIGURE_82586_RESPONSE: // 0x32
+ logerror("CMD_CONFIGURE_82586_RESPONSE");
+ break;
+ case CMD_RECEIVE_PACKET_COMPLETE: // 0x38
+ logerror("CMD_RECEIVE_PACKET_COMPLETE");
+ break;
+ case CMD_TRANSMIT_PACKET_COMPLETE: // 0x39
+ logerror("CMD_TRANSMIT_PACKET_COMPLETE");
+ break;
+ case CMD_NETWORK_STATISTICS_RESPONSE: // 0x3a
+ logerror("CMD_NETWORK_STATISTICS_RESPONSE");
+ break;
+ case CMD_LOAD_MULTICAST_RESPONSE: // 0x3b
+ logerror("CMD_LOAD_MULTICAST_RESPONSE");
+ break;
+ case CMD_DOWNLOAD_PROGRAM_RESPONSE: // 0x3d
+ logerror("CMD_DOWNLOAD_PROGRAM_RESPONSE");
+ break;
+ case CMD_EXECUTE_PROGRAM_RESPONSE: // 0x3e
+ logerror("CMD_EXECUTE_PROGRAM_RESPONSE");
+ break;
+ case CMD_SET_ADDRESS_RESPONSE: // 0x40
+ logerror("CMD_SET_ADDRESS_RESPONSE");
+ break;
+ case CMD_ADAPTER_INFO_RESPONSE: // 0x41
+ logerror("CMD_ADAPTER_INFO_RESPONSE");
+ break;
+ case CMD_MC_17_COMPLETE: // 0x47
+ logerror("CMD_MC_17_COMPLETE");
+ break;
+ case CMD_TRANSMIT_PACKET_18_COMPLETE: // 0x48
+ logerror("CMD_TRANSMIT_PACKET_18_COMPLETE");
+ break;
+ case CMD_MC_E1_RESPONSE: // 0xe1
+ logerror("!!! CMD_MC_E1_RESPONSE");
+ break;
+ case CMD_MC_E2_RESPONSE: // 0xe2
+ logerror("!!! CMD_MC_E2_RESPONSE");
+ break;
+ default:
+ logerror("!!! unexpected Response");
+ }
+ logerror(" (%02x, length=%02x)", m_response.command, m_response.length);
+ for (i = 0; i < m_response.length; i++)
+ {
+ logerror(" %02x", m_response.data.raw[i]);
+ }
+ logerror("\n");
+ }
+}
+
+/***************************************************************************
+ do_receive_command
+ ***************************************************************************/
+
+void threecom3c505_device::do_receive_command()
+{
+ // receive pending and no other command is pending
+ if (m_rx_pending > 0 && !m_command_pending)
+ {
+ if (m_rx_data_buffer.get_length() == 0 && !m_rx_fifo.is_empty())
+ {
+ m_rx_fifo.get(&m_rx_data_buffer);
+ }
+
+ // receive data available ?
+ if (m_rx_data_buffer.get_length() > 0)
+ {
+ LOG2(("do_receive_command - data_length=%x rx_pending=%d",
+ m_rx_data_buffer.get_length(), m_rx_pending));
+
+ m_rx_pending--;
+ set_command_pending(1);
+
+ // preset receive response PCB
+ memcpy(&m_response, &m_rcv_response, sizeof(m_rcv_response));
+
+// m_response.command = CMD_RECEIVE_PACKET_COMPLETE; // 0x38
+// m_response.length = 16;
+// m_response.data.rcv_resp.buf_ofs = htole16(0);
+// m_response.data.rcv_resp.buf_seg = htole16(0);
+// m_response.data.rcv_resp.buf_len = htole16(buf_len);
+
+ // htole16 and friends are not portable beyond Linux. It's named differently on *BSD and differently again on OS X. Avoid!
+ m_response.data.rcv_resp.pkt_len = uint16_to_le(m_rx_data_buffer.get_length());
+ m_response.data.rcv_resp.timeout = 0; // successful completion
+ m_response.data.rcv_resp.status = uint16_to_le(m_rx_data_buffer.get_length() > 0 ? 0 : 0xffff);
+ m_response.data.rcv_resp.timetag = 0; // TODO: time tag
+
+ // compute and check no of bytes to be DMA'ed (must be even)
+ UINT16 buf_len = uint16_from_le(m_response.data.rcv_resp.buf_len) & ~1;
+ if (m_rx_data_buffer.get_length() > buf_len)
+ {
+ LOG1(("do_receive_command !!! buffer size too small (%d < %d)", buf_len, m_rx_data_buffer.get_length()));
+ m_response.data.rcv_resp.pkt_len = uint16_to_le(buf_len);
+ m_response.data.rcv_resp.status = 0xffff;
+ }
+ else
+ {
+ buf_len = (m_rx_data_buffer.get_length() + 1) & ~1;
+ m_response.data.rcv_resp.buf_len = uint16_to_le(buf_len);
+ }
+
+ m_response_length = m_response.length + 2;
+ m_response_index = 0;
+
+ m_status |= ACRF; /* set adapter command register full */
+ if (m_control & CMDE)
+ {
+ set_interrupt(ASSERT_LINE);
+ }
+ }
+ }
+}
+
+/***************************************************************************
+ set_command_pending onoff
+ ***************************************************************************/
+
+void threecom3c505_device::set_command_pending(int state)
+{
+ LOG2(("set_command_pending %d -> %d m_wait_for_ack=%d m_wait_for_nak=%d m_rx_pending=%d%s",
+ m_command_pending, state, m_wait_for_ack, m_wait_for_nak, m_rx_pending, state ? "" :"\n"));
+
+//- verbose = onoff ? 1 : 2;
+
+ switch (state)
+ {
+ case 0:
+ // command is no longer pending
+ m_command_pending = 0;
+
+ // clear previous command byte
+ m_command_buffer[0] = 0;
+
+ m_wait_for_ack = 0;
+ m_wait_for_nak = 0;
+
+ do_receive_command();
+ break;
+ case 1:
+ // command is pending
+ m_command_pending = 1;
+ break;
+ case 2:
+ // wait for nak/ack
+ if (m_microcode_running && m_microcode_version == APOLLO_MC_VERSION_SR10_4)
+ {
+ m_wait_for_nak = 1;
+ }
+ else
+ {
+ m_wait_for_ack = 1;
+ }
+ break;
+ default:
+ LOG(("set_command_pending %d unexpected" , state));
+ break;
+ }
+
+}
+
+/***************************************************************************
+ execute the commands (formerly do_command)
+ ***************************************************************************/
+
+void threecom3c505_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ do_command();
+}
+
+void threecom3c505_device::do_command()
+{
+ pcb_struct &command_pcp = (pcb_struct &) m_command_buffer;
+
+ // default to successful completion
+ m_response.command = command_pcp.command + CMD_RESPONSE_OFFSET;
+ m_response.length = 1;
+ m_response.data.failed = 0; // successful completion
+
+ switch (command_pcp.command)
+ {
+ case CMD_RESET: // 0x00
+ // FIXME: should never occur
+ break;
+
+ case CMD_CONFIGURE_ADAPTER_MEMORY: // 0x01
+ // TODO
+ break;
+
+ case CMD_CONFIGURE_82586: // 0x02
+ m_i82586_config = command_pcp.data.raw[0] + (command_pcp.data.raw[1] << 8);
+ break;
+
+ case CMD_RECEIVE_PACKET: // 0x08
+ // preset response PCB from the Receive Command PCB
+ m_rcv_response.command = CMD_RECEIVE_PACKET_COMPLETE; // 0x38
+ m_rcv_response.length = sizeof(struct Rcv_resp);
+ m_rcv_response.data.rcv_resp.buf_ofs = command_pcp.data.rcv_pkt.buf_ofs;
+ m_rcv_response.data.rcv_resp.buf_seg = command_pcp.data.rcv_pkt.buf_seg;
+ m_rcv_response.data.rcv_resp.buf_len = command_pcp.data.rcv_pkt.buf_len;
+ m_rcv_response.data.rcv_resp.pkt_len = 0;
+ m_rcv_response.data.rcv_resp.timeout = 0;
+ m_rcv_response.data.rcv_resp.status = 0;
+ m_rcv_response.data.rcv_resp.timetag = 0L; // TODO
+
+ m_rx_pending++;
+ set_command_pending(0);
+ return;
+ // break;
+
+ case CMD_TRANSMIT_PACKET_F9:
+ m_response.command = CMD_TRANSMIT_PACKET_COMPLETE;
+ // fall through
+
+ case CMD_TRANSMIT_PACKET: // 0x09
+ case CMD_TRANSMIT_PACKET_18: // 0x18
+ m_response.length = sizeof(struct Xmit_resp);
+ m_response.data.xmit_resp.buf_ofs = 0;
+ m_response.data.xmit_resp.buf_seg = 0;
+ m_response.data.xmit_resp.c_stat = 0; // successful completion
+ m_response.data.xmit_resp.status = 0;
+ break;
+
+ case CMD_EXECUTE_PROGRAM: // 0x0e
+ // m_response.length = 0;
+
+ // FIXME: hack?
+ m_status |= ASF_PCB_END;
+ break;
+
+ case CMD_NETWORK_STATISTICS: // 0x0a
+ m_response.length = sizeof(struct Netstat);
+ m_response.data.netstat.tot_recv = uint16_to_le(m_netstat.tot_recv);
+ m_response.data.netstat.tot_xmit = uint16_to_le(m_netstat.tot_xmit);
+ m_response.data.netstat.err_CRC = uint16_to_le(m_netstat.err_CRC);
+ m_response.data.netstat.err_align = uint16_to_le(m_netstat.err_align);
+ m_response.data.netstat.err_res = uint16_to_le(m_netstat.err_res);
+ m_response.data.netstat.err_ovrrun = uint16_to_le(m_netstat.err_ovrrun);
+ break;
+
+ case CMD_ADAPTER_INFO: // 0x11
+ m_response.length = sizeof(struct Info);
+ // FIXME: using demo data
+ m_response.data.info.minor_vers = 1;
+ m_response.data.info.major_vers = 2;
+ m_response.data.info.ROM_cksum = uint16_to_le(3);
+ m_response.data.info.RAM_sz = uint16_to_le(4);
+ m_response.data.info.free_ofs = uint16_to_le(5);
+ m_response.data.info.free_seg = uint16_to_le(6);
+ break;
+
+ case CMD_LOAD_MULTICAST_LIST:// 0x0b
+ if (command_pcp.length > sizeof(m_multicast_list)
+ || (command_pcp.length % ETHERNET_ADDR_SIZE) != 0)
+ {
+ LOG(("CMD_LOAD_MULTICAST_LIST - unexpected data size %d", command_pcp.length));
+ }
+ else
+ {
+ memset(m_multicast_list, 0, sizeof(m_multicast_list));
+ memcpy(m_multicast_list, command_pcp.data.multicast, command_pcp.length- 2);
+ set_filter_list();
+ }
+ break;
+
+ case CMD_SET_STATION_ADDRESS: // 0x10
+ if (command_pcp.length != sizeof(m_station_address))
+ {
+ LOG(("CMD_SET_STATION_ADDRESS - unexpected data size %d", command_pcp.length));
+ memset(m_station_address, 0, sizeof(m_station_address));
+ }
+ else
+ {
+ memcpy(m_station_address, command_pcp.data.eth_addr, command_pcp.length);
+ }
+ set_filter_list();
+ set_mac((char *) m_station_address);
+ break;
+
+ case CMD_MC_17: // 0x17
+ m_microcode_running = 1;
+ break;
+
+ case CMD_DOWNLOAD_PROGRAM: // 0x0d
+ UINT16 mc_version = m_program_buffer.get_word(1);
+ switch (mc_version)
+ {
+ case APOLLO_MC_VERSION_SR10_2:
+ case APOLLO_MC_VERSION_SR10_4:
+ m_microcode_version = mc_version;
+ break;
+ default:
+ m_microcode_version = 0;
+ LOG(("CMD_DOWNLOAD_PROGRAM - unexpected microcode version %04x", mc_version));
+ break;
+ }
+ // return microcode version as program id
+ m_response.length = 2;
+ m_response.data.raw[0] = m_microcode_version & 0xff;
+ m_response.data.raw[1] = (m_microcode_version >> 8) & 0xff;
+ break;
+ }
+
+ m_response_index = 0;
+ m_response_length = m_response.length + 2;
+
+ m_status |= ACRF; /* set adapter command register full */
+ if (m_control & CMDE)
+ {
+ set_interrupt(ASSERT_LINE);
+ }
+}
+
+/***************************************************************************
+ ethernet_packet_is_for_me - check if ethernet address is for me
+ ***************************************************************************/
+
+int threecom3c505_device::ethernet_packet_is_for_me(const UINT8 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;
+ static const UINT8 broadcast_address[ETHERNET_ADDR_SIZE] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ // accept all packets if RECV_PROMISC is set
+ if (m_i82586_config & RECV_PROMISC)
+ {
+ return 1;
+ }
+
+ // skip Ethernet broadcast packets if RECV_BROAD is not set
+ if (!(m_i82586_config & RECV_BROAD) && memcmp(mac_address,
+ broadcast_address, ETHERNET_ADDR_SIZE) == 0)
+ {
+ return 0;
+ }
+
+ // skip Ethernet multicast packets if RECV_MULTI is not set
+ if (!(m_i82586_config & RECV_MULTI) && (mac_address[0] & 0x01) != 0)
+ {
+ return 0;
+ }
+
+ for (i = 0; i + ETHERNET_ADDR_SIZE < sizeof(m_filter_list); i += ETHERNET_ADDR_SIZE)
+ {
+ if (memcmp(mac_address, m_filter_list + i, ETHERNET_ADDR_SIZE) == 0)
+ {
+ return 1;
+ }
+ }
+ for (i = 0; i + ETHERNET_ADDR_SIZE < sizeof(m_multicast_list); i += ETHERNET_ADDR_SIZE)
+ {
+ if (memcmp(mac_address, m_multicast_list + i, ETHERNET_ADDR_SIZE) == 0)
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/***************************************************************************
+ recv_cb - receive callback - receive and process an ethernet packet
+ ***************************************************************************/
+
+void threecom3c505_device::recv_cb(UINT8 *data, int length)
+{
+ if (length < ETHERNET_ADDR_SIZE || !ethernet_packet_is_for_me(data))
+ {
+ // skip packet
+ }
+ else if (!m_rx_fifo.put(data, length))
+ {
+ m_netstat.tot_recv++;
+ m_netstat.err_ovrrun++;
+ // fifo overrun
+ LOG1(("recv_cb: data_length=%x !!! RX FIFO OVERRUN !!!", length));
+
+ }
+ else
+ {
+ m_netstat.tot_recv++;
+ LOG2(("recv_cb: data_length=%x m_rx_pending=%d", length, m_rx_pending));
+
+ do_receive_command();
+ }
+}
+
+/***************************************************************************
+ write_command_port
+***************************************************************************/
+
+void threecom3c505_device::write_command_port(UINT8 data)
+{
+ LOG2(("writing 3C505 command port %02x - m_status=%02x m_control=%02x m_command_index=%02x",
+ data, m_status, m_control, m_command_index));
+
+ if (m_command_index == 0)
+ {
+ switch (data)
+ {
+ case 0:
+ LOG2(("!!! writing 3C505 Command Register = %02x", data));
+ // spurious data; reset?
+ break;
+
+ case CMD_TRANSMIT_PACKET_F9:
+ // read data length from data port (not from command port)
+ m_tx_data_buffer.reset();
+ m_status |= HRDY; /* data register ready */
+ m_command_buffer[m_command_index++] = data;
+ set_command_pending(1);
+ break;
+
+ default:
+ m_command_buffer[m_command_index++] = data;
+ set_command_pending(1);
+ break;
+ }
+ }
+ else if ((m_control & HSF_PCB_MASK) != HSF_PCB_END)
+ {
+ m_command_buffer[m_command_index++] = data;
+ }
+ else
+ {
+ m_status &= ~ASF_PCB_MASK;
+ m_status |= (data == m_command_index) ? ASF_PCB_END : ASF_PCB_NAK;
+
+ log_command();
+
+ switch (m_command_buffer[0])
+ {
+ case CMD_TRANSMIT_PACKET_18:
+ // read transmit data into m_tx_data_buffer
+ m_tx_data_buffer.reset();
+ m_tx_data_length = m_command_buffer[2] + (m_command_buffer[3] << 8);
+ m_status |= HRDY; /* data register ready */
+ break;
+
+ case CMD_TRANSMIT_PACKET:
+ // read transmit data into m_tx_data_buffer
+ m_tx_data_buffer.reset();
+ m_tx_data_length = m_command_buffer[6] + (m_command_buffer[7] << 8);
+ m_status |= HRDY; /* data register ready */
+ break;
+
+ case CMD_DOWNLOAD_PROGRAM:
+ // read program data into m_program_buffer
+ m_program_buffer.reset();
+ m_program_length = m_command_buffer[2] + (m_command_buffer[3] << 8);
+ m_status |= HRDY; /* data register ready */
+ break;
+
+ case CMD_NETWORK_STATISTICS: // 0x0a
+ case CMD_EXECUTE_PROGRAM: // 0x0e
+ case CMD_ADAPTER_INFO: // 0x11
+ // delay command execution
+ m_do_command_timer->adjust(attotime::from_usec(100));
+ break;
+
+ default:
+ do_command();
+ break;
+ }
+ }
+
+ m_status |= HCRE; /* command register empty */
+}
+
+/***************************************************************************
+ read_command_port
+ ***************************************************************************/
+
+UINT8 threecom3c505_device::read_command_port()
+{
+ UINT8 data;
+
+ // the interrupt request is cleared when the Command Register is read
+ set_interrupt(CLEAR_LINE);
+
+ if (m_response_index == 0)
+ {
+ data = m_response.command;
+ }
+ else if (m_response_index == 1)
+ {
+ data = m_response.length;
+ }
+ else if (m_response_index < m_response_length)
+ {
+ data = m_response.data.raw[m_response_index - 2];
+ }
+ else if (m_response_index == m_response_length)
+ {
+ data = m_response.length + 2;
+ }
+ else if (m_response_index == m_response_length + 1 /*&& m_microcode_running*/)
+ {
+ // FIXME: special for SR10.4 microcode, content doesn't matter?
+ data = 0; // ?
+ m_response_index++;
+
+ m_status &= ~ACRF; /* the adapter command register is no longer full */
+
+ LOG2(("read_command_port: !!! reading 3C505 Command Register = %02x - m_status=%02x m_control=%02x",
+ data, m_status, m_control));
+
+ // wait for nak in control register
+ set_command_pending(2);
+ }
+ else
+ {
+ // should never happen
+ data = 0; // 0xff;
+ LOG(("read_command_port: unexpected reading Command Register at index %04x", m_response_index));
+ }
+
+ if (m_response_index <= m_response_length + 1)
+ {
+ if (++m_response_index == m_response_length)
+ {
+ m_status = (m_status & ~ASF_PCB_MASK) | ASF_PCB_END;
+ }
+ else if (m_response_index == m_response_length + 1)
+ {
+ log_response();
+
+ switch (m_response.command)
+ {
+ case CMD_MC_E1_RESPONSE:
+ m_status |= HRDY; /* data register ready */
+ // prepend data length
+ m_rx_data_index = -2;
+ break;
+
+ case CMD_RECEIVE_PACKET_COMPLETE:
+ m_status |= HRDY; /* data register ready */
+ m_rx_data_index = 0;
+ break;
+
+ case CMD_TRANSMIT_PACKET_COMPLETE:
+ case CMD_TRANSMIT_PACKET_18_COMPLETE:
+ m_netstat.tot_xmit++;
+
+ if (!send(m_tx_data_buffer.get_data(), m_tx_data_buffer.get_length()))
+ {
+ // FIXME: failed to send the Ethernet packet
+ LOG(("read_command_port(): !!! failed to send Ethernet packet"));
+ }
+
+ if (!tx_data(this, m_tx_data_buffer.get_data(), m_tx_data_buffer.get_length()))
+ {
+ // FIXME: failed to transmit the Ethernet packet
+ LOG(("read_command_port(): !!! failed to transmit Ethernet packet"));
+ }
+
+ m_tx_data_buffer.reset();
+ if (m_command_buffer[0] != CMD_TRANSMIT_PACKET_F9)
+ {
+ set_command_pending(2);
+ }
+ break;
+
+ case CMD_DOWNLOAD_PROGRAM_RESPONSE:
+ m_program_buffer.reset();
+ set_command_pending(2);
+ break;
+
+ default:
+ set_command_pending(2);
+ break;
+ }
+ }
+ }
+ return data;
+}
+
+/***************************************************************************
+ write_data_port
+ ***************************************************************************/
+
+void threecom3c505_device::write_data_port(UINT8 data)
+{
+ if (m_control & FLSH)
+ {
+ // flush input data
+ }
+ else if ((m_status & HRDY) == 0)
+ {
+ // this happened in ether.dex Test 20/1
+ LOG(("write_data_port: failed to write tx data (data register not ready), data length=%x status=%x",
+ m_tx_data_buffer.get_length(), m_status));
+ }
+
+#if 0
+ else if (m_command_buffer[0] == CMD_MC_F8)
+ {
+ // FIXME: what does it do?
+ LOG(("write_data_port: !!! TODO: CMD_MC_F8 !!! command=%x data=%02x", m_command_buffer[0], data));
+ }
+#endif
+
+ else if (m_command_buffer[0] == CMD_TRANSMIT_PACKET_F9)
+ {
+ switch (m_command_index)
+ {
+ case 1:
+ m_command_buffer[m_command_index++] = data;
+ break;
+ case 2:
+ m_command_buffer[m_command_index++] = data;
+ m_tx_data_length = m_command_buffer[1] + (m_command_buffer[2] << 8);
+ log_command();
+ break;
+ default:
+ if (!m_tx_data_buffer.append(data))
+ {
+ LOG(("write_data_port: failed to write tx data (buffer size exceeded), data length=%x",
+ m_tx_data_buffer.get_length()));
+ }
+ if (m_tx_data_buffer.get_length() == m_tx_data_length)
+ {
+ // CMD_TRANSMIT_PACKET_COMPLETE
+ m_tx_data_buffer.log("Tx Data");
+ do_command();
+ }
+ break;
+ }
+ }
+ else if (m_command_buffer[0] == CMD_TRANSMIT_PACKET || //
+ m_command_buffer[0] == CMD_TRANSMIT_PACKET_18)
+ {
+ if (!m_tx_data_buffer.append(data))
+ {
+ LOG(("write_data_port: failed to write tx data (buffer size exceeded), data length=%x",
+ m_tx_data_buffer.get_length()));
+ }
+
+ if (m_tx_data_buffer.get_length() == m_tx_data_length)
+ {
+ // CMD_TRANSMIT_PACKET_COMPLETE
+ m_tx_data_buffer.log("Tx Data");
+ do_command();
+ }
+ }
+ else if (m_command_buffer[0] == CMD_DOWNLOAD_PROGRAM)
+ {
+ if (!m_program_buffer.append(data))
+ {
+ LOG(("write_data_port: failed to write program data (buffer size exceeded), data length=%x",
+ m_program_buffer.get_length()));
+ }
+
+ if (m_program_buffer.get_length() == m_program_length)
+ {
+ m_program_buffer.log("Program Data");
+ do_command();
+ }
+ }
+ else if (m_tx_data_buffer.get_length() < PORT_DATA_FIFO_SIZE)
+ {
+ // write to data fifo
+ if (!m_tx_data_buffer.append(data))
+ {
+ LOG(("write_data_port: failed to write tx data (buffer size exceeded), data length=%x",
+ m_tx_data_buffer.get_length()));
+ }
+ }
+ else
+ {
+ LOG(("write_data_port: unexpected command %02x data=%02x", m_command_buffer[0], data));
+ }
+
+ if (m_command_buffer[0] != CMD_DOWNLOAD_PROGRAM &&
+ m_tx_data_buffer.get_length() >= PORT_DATA_FIFO_SIZE
+ && m_tx_data_buffer.get_length() >= m_tx_data_length)
+ {
+ m_status &= ~HRDY; /* data register not ready */
+ if (m_tx_data_buffer.get_length() > PORT_DATA_FIFO_SIZE
+ && m_tx_data_buffer.get_length() > m_tx_data_length)
+ {
+ LOG(("write_data_port: port_data tx fifo exhausted, data length=%x status=%x",
+ m_tx_data_buffer.get_length(), m_status));
+ }
+ }
+}
+
+/***************************************************************************
+ read_data_port
+ ***************************************************************************/
+
+UINT8 threecom3c505_device::read_data_port()
+{
+ UINT8 data;
+ UINT16 data_length = m_rx_data_buffer.get_length();
+ // DomainOS will read words (i.e. even number of bytes); must handle packets with odd byte length
+ UINT16 even_data_length = (data_length + 1) & ~1;
+
+ if (m_rx_data_index < even_data_length)
+ {
+ // eventually prepend data length (for CMD_MC_E1_RESPONSE)
+ data = m_rx_data_index == -2 ? (data_length & 0xff) : //
+ m_rx_data_index == -1 ? (data_length << 8) : //
+ m_rx_data_buffer.get(m_rx_data_index);
+
+ m_rx_data_index++;
+
+ if (m_rx_data_index == even_data_length)
+ {
+ m_status &= ~HRDY; /* data register no longer ready */
+ m_rx_data_buffer.log("Rx Data");
+ m_rx_data_buffer.reset();
+ set_command_pending(2);
+ }
+ }
+ else
+ {
+ // FIXME: should never happen
+ data = 0xff;
+ LOG(("read_data_port: unexpected reading data at index %04x)", m_rx_data_index));
+ }
+ return data;
+}
+
+/***************************************************************************
+ write_control_port
+ ***************************************************************************/
+
+void threecom3c505_device::write_control_port(UINT8 data)
+{
+ switch (data & (ATTN | FLSH))
+ {
+ case ATTN:
+ LOG2(("write_control_port %02x - Soft Reset", data));
+ // TODO: soft reset
+ break;
+
+ case FLSH:
+ LOG2(("write_control_port %02x - Flush Data Register", data));
+ // flush data register
+ if (data & DIR_)
+ {
+ m_status &= ~HRDY; /* data register not ready */
+ }
+ else
+ {
+ // download to adapter
+ m_status |= HRDY; /* data register ready */
+
+ // flush data register (reset tx data fifo)
+ // m_tx_data_length = 0;
+ m_tx_data_buffer.reset();
+ }
+ break;
+
+ case ATTN | FLSH:
+ LOG2(("write_control_port %02x - Reset Adapter", data));
+ device_reset();
+ break;
+
+ case 0:
+ LOG2(("write_control_port %02x", data));
+
+ // end reset
+ if ((m_control & (ATTN | FLSH)) == (ATTN | FLSH))
+ {
+ m_status |= ASF_PCB_END;
+ m_status |= HRDY; /* 20 byte data fifo is empty */
+ }
+
+ if (data == DIR_)
+ {
+ // why?? dex ether 20 expects HRDY
+ m_status |= HRDY; /* data register ready */
+ }
+ break;
+ }
+
+ // propagate DIR_ from Control to Status register
+ m_status = (m_status & ~DIR_) | (data & DIR_);
+
+ switch (data & HSF_PCB_MASK)
+ {
+ case HSF_PCB_ACK: // HSF1
+ if (m_wait_for_ack)
+ {
+ set_command_pending(0);
+ }
+ break;
+
+ case HSF_PCB_END: // (HSF2|HSF1)
+ m_status &= ~ACRF; /* adapter command register is not full */
+ // fall through
+
+ case HSF_PCB_NAK: // HSF2
+ if (m_microcode_running)
+ {
+ if (m_wait_for_nak)
+ {
+ set_command_pending(0);
+ }
+ m_status = (m_status & ~ASF_PCB_MASK) | ASF_PCB_ACK;
+ }
+ break;
+
+ default: // 0
+ m_command_index = 0;
+ m_status |= HCRE; /* host command register is empty */
+ break;
+ }
+
+ m_control = data;
+}
+
+/***************************************************************************
+ read_status_port
+ ***************************************************************************/
+
+UINT8 threecom3c505_device::read_status_port()
+{
+ UINT8 data = m_status;
+ m_status &= ~ASF_PCB_MASK;
+
+ switch (data & ASF_PCB_MASK)
+ {
+ case ASF_PCB_END:
+ m_status |= ASF_PCB_ACK;
+ break;
+ }
+ return data;
+}
+
+
+/***************************************************************************
+ write_port
+ ***************************************************************************/
+
+WRITE16_MEMBER(threecom3c505_device::write)
+{
+ // make byte offset
+ offset *= 2;
+
+ m_reg[offset & 0x0f] = data;
+ LOG2(("writing 3C505 Register at offset=%02x with mem_mask=%04x = %04x", offset, mem_mask, data));
+
+ switch (offset)
+ {
+ case PORT_COMMAND: /* 0x00 read/write, 8-bit */
+ write_command_port(data);
+ break;
+ case PORT_AUXDMA: /* 0x02 write only, 8-bit */
+ break;
+ case PORT_DATA: /* 0x04 read/write, 16-bit */
+ write_data_port(data & 0xff);
+ write_data_port(data >> 8);
+ break;
+ case PORT_CONTROL: /* 0x06 read/write, 8-bit */
+ write_control_port(data);
+ break;
+ default:
+ break;
+ }
+}
+
+/***************************************************************************
+ read_port
+ ***************************************************************************/
+
+READ16_MEMBER(threecom3c505_device::read)
+{
+ // data to omit excessive logging
+ static UINT16 last_data = 0xff;
+ static UINT32 last_pc = 0;
+
+ // make byte offset
+ offset *= 2;
+
+ UINT16 data = m_reg[offset & 0x0f];
+ switch (offset)
+ {
+ case PORT_COMMAND: /* 0x00 read/write, 8-bit */
+ data = read_command_port();
+ break;
+ case PORT_STATUS: /* 0x02 read only, 8-bit */
+ data = read_status_port();
+
+ // omit excessive logging
+ if (data == last_data)
+ {
+ UINT32 pc = space.device().safe_pcbase();
+ if (pc == last_pc)
+ {
+ return data;
+ }
+ last_pc = pc;
+ }
+ last_data = data;
+ break;
+ case PORT_DATA: /* 0x04 read/write, 16-bit */
+ data = read_data_port();
+ data |= (read_data_port() << 8);
+ break;
+ case PORT_CONTROL: /* 0x06 read/write, 8-bit */
+ data = m_control;
+ break;
+ default:
+ break;
+ }
+
+ LOG2(("reading 3C505 Register at offset=%02x with mem_mask=%04x = %04x", offset, mem_mask, data));
+
+ return data;
+}
+
+void threecom3c505_device::set_verbose(int on_off)
+{
+ verbose = on_off == 0 ? 0 : VERBOSE > 1 ? VERBOSE : 1;
+}
+
+int threecom3c505_device::tx_data(device_t *device, const UINT8 data[], int length)
+{
+ LOG1(("threecom3c505_device::tx_data length=%d", length));
+ return 1;
+}
+
+int threecom3c505_device::setfilter(device_t *device, int node_id)
+{
+ return 0;
+}