From cf64b0fed3c59d2aa5f0a7ccb272ecf758c2fcfd Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Fri, 5 Apr 2019 19:04:52 +0700 Subject: [PATCH] 3c505: replace hle (nw) For review/comment especially by @rb6502 and Hans Ostermeyer because it's: * a rewrite/replacement of prior work, and proposes taking over the copyright as well; and * untested on Apollo at the moment because the driver is crashing while trying to install Domain/OS (unrelated to this device). Testing with the 3c505.exe diagnostic program on MS-DOS passes the Group 1 & 2 tests successfully when using 16-bit DMA channel. It intermittently fails the Group 2 transmit test when using an 8-bit channel. --- src/devices/bus/isa/3c505.cpp | 1995 +++-------------------------- src/devices/bus/isa/3c505.h | 303 +---- src/devices/bus/isa/isa_cards.cpp | 1 - src/mame/machine/apollo.cpp | 11 +- 4 files changed, 228 insertions(+), 2082 deletions(-) diff --git a/src/devices/bus/isa/3c505.cpp b/src/devices/bus/isa/3c505.cpp index 6eb4a99e8b8..622a11d48dd 100644 --- a/src/devices/bus/isa/3c505.cpp +++ b/src/devices/bus/isa/3c505.cpp @@ -1,1722 +1,46 @@ // license:BSD-3-Clause -// copyright-holders:Hans Ostermeyer,R. Belmont +// copyright-holders:Patrick Mackinlay + /* - * 3c505.c - 3COM 3C505 ethernet controller (for Apollo DN3x00) + * 3Com EtherLink Plus (3C505) ethernet adapter. The documentation refers to 3 + * revisions of the hardware and firmware; this implementation and the firmware + * are from a revision 3 board. The card was designed to work when installed in + * both 8-bit and 16-bit ISA bus slots. * - * Created on: August 27, 2010 - * Author: Hans Ostermeyer - * ISA conversion by R. Belmont + * Sources: * - * 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 "emu.h" -#include "3c505.h" - -#define VERBOSE 0 - -static int verbose = VERBOSE; - -#define LOG(d,x) { d->logerror ("%s: ", cpu_context()); d->logerror x; d->logerror ("\n"); } -#define LOG1(d,x) { if (verbose > 0) LOG(d,x)} -#define LOG2(d,x) { if (verbose > 1) LOG(d,x)} - -#ifdef LSB_FIRST -static uint16_t uint16_to_le(uint16_t value) -{ - return value; -} - -static uint16_t uint16_from_le(uint16_t value) -{ - return value; -} -#else -static uint16_t uint16_to_le(uint16_t value) -{ - return ((value&0x00ff)<<8)|((value&0xff00)>>8); -} - -static uint16_t uint16_from_le(uint16_t 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 - ***************************************************************************/ - -constexpr unsigned threecom3c505_device::ETH_BUFFER_SIZE; - -// device type definition -DEFINE_DEVICE_TYPE(ISA16_3C505, threecom3c505_device, "3c505", "3Com 3C505 Network Adaptor") -DEFINE_DEVICE_TYPE(ISA16_3C505_LLE, isa16_3c505_device, "3c505_lle", "3Com EtherLink Plus") - -//------------------------------------------------- -// threecom3c505_device - constructor -//------------------------------------------------- - -threecom3c505_device::threecom3c505_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : threecom3c505_device(mconfig, ISA16_3C505, tag, owner, clock) -{ -} - -threecom3c505_device::threecom3c505_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), - device_isa16_card_interface(mconfig, *this), - m_iobase(*this, "IO_BASE"), - m_irqdrq(*this, "IRQ_DRQ"), - m_romopts(*this, "ROM_OPTS"), - m_status(0), m_control(0), m_command_index(0), m_command_pending(0), m_wait_for_ack(0), m_wait_for_nak(0), m_rx_data_index(0), m_rx_pending(0), m_tx_data_length(0), m_program_length(0), m_response_length(0), m_response_index(0), m_microcode_version(0), m_microcode_running(0), m_i82586_config(0), irq_state(), m_do_command_timer(nullptr), m_installed(false), m_irq(0), m_drq(0) -{ -} - -ioport_constructor threecom3c505_device::device_input_ports() const -{ - return INPUT_PORTS_NAME( tc3c505_port ); -} - -const tiny_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(this,("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, nullptr); -} - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - -void threecom3c505_device::device_reset() -{ - LOG1(this,("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, 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, "threecom3c505", "threecom3c505"); - } - - m_installed = true; - } -} - -/*************************************************************************** - cpu_context - return a string describing the current CPU context - ***************************************************************************/ - -std::string threecom3c505_device::cpu_context() const -{ - osd_ticks_t t = osd_ticks(); - int s = (t / osd_ticks_per_second()) % 3600; - int ms = (t / (osd_ticks_per_second() / 1000)) % 1000; - - return string_format("%d.%03d %s", s, ms, machine().describe_context()); -} - -/*------------------------------------------------- - logerror - log an error message (w/o device tags) - -------------------------------------------------*/ - -template -void threecom3c505_device::logerror(Format &&fmt, Params &&... args) const -{ - machine().logerror(std::forward(fmt), std::forward(args)...); -} - -//************************************************************************** -// data_buffer -//************************************************************************** - -threecom3c505_device::data_buffer::data_buffer() : - m_device(nullptr), m_length(0) -{ -} - -void threecom3c505_device::data_buffer::start(threecom3c505_device *device, int32_t size) -{ - m_device = device; - LOG2(device,("start threecom3c505_device::data_buffer with size %0x", size)); - m_data.resize(size); -} - -void threecom3c505_device::data_buffer::reset() -{ - LOG2(m_device,("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_t 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; - m_device->logerror("%s: %s (length=%02x)", m_device->cpu_context(), title, m_length); - for (i = 0; i < m_length; i++) - { - m_device->logerror(" %02x", m_data[i]); - if (i >= 1023) - { - m_device->logerror(" ..."); - break; - } - } - m_device->logerror("\n"); - } -} - -//************************************************************************** -// data_buffer fifo -//************************************************************************** - -threecom3c505_device::data_buffer_fifo::data_buffer_fifo() : - m_device(nullptr), - m_size(0), m_count(0), m_get_index(0), m_put_index(0) -{ -} - -void threecom3c505_device::data_buffer_fifo::start( - threecom3c505_device *device, int32_t size, int32_t db_size) -{ - m_device = device; - LOG2(m_device,("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(m_device,("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_t data[], const int length) -{ - uint16_t next_index = (m_put_index + 1) % m_size; - - LOG2(m_device,("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(m_device,("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(m_device,("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(this,("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(this,("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(this,("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_t buf_len = uint16_from_le(m_response.data.rcv_resp.buf_len) & ~1; - if (m_rx_data_buffer.get_length() > buf_len) - { - LOG1(this,("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(this,("set_command_pending %d -> %d m_wait_for_ack=%d m_wait_for_nak=%d m_rx_pending=%d", - m_command_pending, state, m_wait_for_ack, m_wait_for_nak, m_rx_pending)); - -//- 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(this,("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(this,("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(this,("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_t 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(this,("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_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; - static const uint8_t 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_t *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(this,("recv_cb: data_length=%x !!! RX FIFO OVERRUN !!!", length)); - - } - else - { - m_netstat.tot_recv++; - LOG2(this,("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_t data) -{ - LOG2(this,("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(this,("!!! 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_t threecom3c505_device::read_command_port() -{ - uint8_t 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(this,("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(this,("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++; - - // append the Ethernet Frame Check Sequence - // see also http://www.edaboard.com/thread120700.html - { - // compute the Ethernet Frame Check Sequence - static const uint32_t crc_table[] = - { 0x4DBDF21C, 0x500AE278, 0x76D3D2D4, 0x6B64C2B0, - 0x3B61B38C, 0x26D6A3E8, 0x000F9344, 0x1DB88320, - 0xA005713C, 0xBDB26158, 0x9B6B51F4, 0x86DC4190, - 0xD6D930AC, 0xCB6E20C8, 0xEDB71064, 0xF0000000 }; - uint32_t n, crc = 0; - for (n = 0; n < m_tx_data_buffer.get_length(); n++) - { - uint8_t data = m_tx_data_buffer.get(n); - crc = (crc >> 4) ^ crc_table[(crc ^ (data >> 0)) & 0x0f]; /* lower nibble */ - crc = (crc >> 4) ^ crc_table[(crc ^ (data >> 4)) & 0x0f]; /* upper nibble */ - } - - // append the Ethernet Frame Check Sequence - for (n = 0; n < 4; n++) - { - m_tx_data_buffer.append(crc & 0xff); - crc >>= 8; - } - } - - if (!send(m_tx_data_buffer.get_data(), m_tx_data_buffer.get_length())) - { - // FIXME: failed to send the Ethernet packet - LOG(this,("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(this,("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_t data) -{ - if (m_control & FLSH) - { - // flush input data - } - else if ((m_status & HRDY) == 0) - { - // this happened in ether.dex Test 20/1 - LOG(this,("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(this,("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(this,("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(this,("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(this,("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(this,("write_data_port: failed to write tx data (buffer size exceeded), data length=%x", - m_tx_data_buffer.get_length())); - } - } - else - { - LOG(this,("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(this,("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_t threecom3c505_device::read_data_port() -{ - uint8_t data; - uint16_t 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_t 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(this,("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_t data) -{ - switch (data & (ATTN | FLSH)) - { - case ATTN: - LOG2(this,("write_control_port %02x - Soft Reset", data)); - // TODO: soft reset - break; - - case FLSH: - LOG2(this,("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(this,("write_control_port %02x - Reset Adapter", data)); - device_reset(); - break; - - case 0: - LOG2(this,("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_t threecom3c505_device::read_status_port() -{ - uint8_t 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(this,("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_t last_data = 0xff; - static uint32_t last_pc = 0; - - // make byte offset - offset *= 2; - - uint16_t 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) - { - // FIXME: space.device().state().pcbase() will crash mame with SIGSEGV (since mame0197) - uint32_t pc = 0; // space.device().state().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(this,("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_t data[], int length) -{ - LOG1(this,("threecom3c505_device::tx_data length=%d", length)); - return 1; -} - -int threecom3c505_device::setfilter(device_t *device, int node_id) -{ - return 0; -} - -/* - * The following device is a low-level reimplementation of the 3c505, which - * should entirely replace the HLE one above when it's been tested and proven - * working. + * 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 * * TODO - * - testing (unable to install Domain/OS on Apollo, and not yet able to get - * 3c505.exe diagnostic program running on MS-DOS) - * - data DMA functionality unimplemented + * - resolve intermittent diagnostics bug on 8-bit dma channels + * - testing on Apollo Domain/OS * - externalize mac address * - 8 bit isa slot support * - revision 1.0 and 2.0 hardware/firmware variants * - 8023 loopback mode */ -#undef LOG -#undef VERBOSE +#include "emu.h" +#include "3c505.h" #define LOG_GENERAL (1U << 0) #define LOG_REG (1U << 1) +#define LOG_DATA (1U << 2) -#define VERBOSE (0) +//#define VERBOSE (LOG_GENERAL|LOG_REG|LOG_DATA) #include "logmacro.h" +DEFINE_DEVICE_TYPE(ISA16_3C505, isa16_3c505_device, "3c505", "3Com EtherLink Plus") + ROM_START(3c505) ROM_REGION16_LE(0x04000, "system", 0) - // TODO: probably a revision 3.0 firmware dump - ROMX_LOAD("0729-12_a.3h", 0x00000, 0x02000, CRC(5415fccd) SHA1(6a42d7f3acdb3e0213e1037fee1864819ac33991), ROM_SKIP(1)) - ROMX_LOAD("0729-62_a.3f", 0x00001, 0x02000, CRC(4240bd9d) SHA1(015d2f7282def85681bcf1a7c5a7f501a16d5a6c), ROM_SKIP(1)) + // this system firmware reports revision 0x0301 (3.1) + ROM_LOAD16_BYTE("0729-12_a.3h", 0x00000, 0x02000, CRC(5415fccd) SHA1(6a42d7f3acdb3e0213e1037fee1864819ac33991)) + ROM_LOAD16_BYTE("0729-62_a.3f", 0x00001, 0x02000, CRC(4240bd9d) SHA1(015d2f7282def85681bcf1a7c5a7f501a16d5a6c)) ROM_REGION(0x02000, "host", 0) ROM_SYSTEM_BIOS(0, "unused", "Unused") @@ -1811,8 +135,8 @@ static INPUT_PORTS_START(3c505) PORT_DIPSETTING( 0x0e, "IRQ 14") PORT_DIPSETTING( 0x0f, "IRQ 15") - // TODO: dma channel uses two jumpers for each channel: mode selection? - PORT_DIPNAME(0x70, 0x60, "DRQ") + // TODO: uses two jumpers for each channel: dma mode selection? + PORT_DIPNAME(0x70, 0x50, "DRQ") PORT_DIPSETTING( 0x00, "none") // 8 or 16 bit slots PORT_DIPSETTING( 0x10, "DRQ 1") @@ -1822,17 +146,43 @@ static INPUT_PORTS_START(3c505) PORT_DIPSETTING( 0x60, "DRQ 6") PORT_DIPSETTING( 0x70, "DRQ 7") - // 8-position jumper block marked EN,13-19 corresponds to address line decode + // 8-position jumper block marked EN,13-19 decodes address lines PORT_START("ROM_OPTS") - PORT_DIPNAME(0x01, 0x01, "ROM Enable") + PORT_DIPNAME(0x01, 0x00, "ROM Enable") PORT_DIPSETTING( 0x00, DEF_STR(Off)) PORT_DIPSETTING( 0x01, DEF_STR(On)) + PORT_DIPNAME(0xfe, 0x00, "ROM Base") - PORT_DIPSETTING( 0x00, "0000h") - PORT_DIPSETTING( 0x02, "2000h") - PORT_DIPSETTING( 0x04, "4000h") - PORT_DIPSETTING( 0x06, "6000h") - // TODO: additional addresses + // Apollo host ROM addresses + PORT_DIPSETTING( 0x00, "00000h") + PORT_DIPSETTING( 0x02, "02000h") + PORT_DIPSETTING( 0x04, "04000h") + PORT_DIPSETTING( 0x06, "06000h") + + // conventional PC option ROM addresses + PORT_DIPSETTING( 0xc8, "C8000h") + PORT_DIPSETTING( 0xca, "CA000h") + PORT_DIPSETTING( 0xcc, "CC000h") + PORT_DIPSETTING( 0xce, "CE000h") + PORT_DIPSETTING( 0xd0, "D0000h") + PORT_DIPSETTING( 0xd2, "D2000h") + PORT_DIPSETTING( 0xd4, "D4000h") + PORT_DIPSETTING( 0xd6, "D6000h") + PORT_DIPSETTING( 0xd8, "D8000h") + PORT_DIPSETTING( 0xda, "DA000h") + PORT_DIPSETTING( 0xdc, "DC000h") + PORT_DIPSETTING( 0xde, "DE000h") + PORT_DIPSETTING( 0xe0, "E0000h") + PORT_DIPSETTING( 0xe2, "E2000h") + PORT_DIPSETTING( 0xe4, "E4000h") + PORT_DIPSETTING( 0xe6, "E6000h") + PORT_DIPSETTING( 0xe8, "E8000h") + PORT_DIPSETTING( 0xea, "EA000h") + PORT_DIPSETTING( 0xec, "EC000h") + PORT_DIPSETTING( 0xee, "EE000h") + PORT_DIPSETTING( 0xf0, "F0000h") + PORT_DIPSETTING( 0xf2, "F2000h") + PORT_DIPSETTING( 0xf4, "F4000h") PORT_START("TEST") PORT_DIPNAME(0x01, 0x00, "TEST Mode") @@ -1841,7 +191,7 @@ static INPUT_PORTS_START(3c505) INPUT_PORTS_END isa16_3c505_device::isa16_3c505_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : device_t(mconfig, ISA16_3C505_LLE, tag, owner, clock) + : device_t(mconfig, ISA16_3C505, tag, owner, clock) , device_isa16_card_interface(mconfig, *this) , m_cpu(*this, "cpu") , m_net(*this, "net") @@ -1906,7 +256,9 @@ void isa16_3c505_device::device_start() //save_item(NAME(m_data); + save_item(NAME(m_cpu_drq_asserted)); save_item(NAME(m_cpu_irq_asserted)); + save_item(NAME(m_isa_drq_asserted)); save_item(NAME(m_isa_irq_asserted)); } @@ -1915,9 +267,7 @@ void isa16_3c505_device::device_reset() if (!m_installed) { u16 const base = m_iobase->read(); - m_isa->install16_device(base, base | 0xf, - read16_delegate(FUNC(isa16_3c505_device::host_r), this), - write16_delegate(FUNC(isa16_3c505_device::host_w), this)); + m_isa->install_device(base, base | 0xf, *this, &isa16_3c505_device::map_isa); m_isa_irq = m_irqdrq->read() & 0xf; m_isa_drq = (m_irqdrq->read() >> 4) & 0x7; @@ -1926,9 +276,12 @@ void isa16_3c505_device::device_reset() { offs_t const rom_base = (m_romopts->read() & 0xfe) << 12; - m_isa->install_rom(this, rom_base, rom_base | 0x01fff, "host", "host"); + if (m_isa->is_option_rom_space_available(rom_base, 0x2000)) + m_isa->install_rom(this, rom_base, rom_base | 0x01fff, "host", "host"); } + m_isa->set_dma_channel(m_isa_drq, this, true); + m_installed = true; } @@ -1948,10 +301,13 @@ void isa16_3c505_device::device_reset() m_hdr = 0; m_data.clear(); - update_rdy(m_acr, m_hcr); + m_cpu_drq_asserted = false; m_cpu_irq_asserted = false; + m_isa_drq_asserted = false; m_isa_irq_asserted = false; + + update_rdy(m_acr, m_hcr); } void isa16_3c505_device::map_main(address_map &map) @@ -1996,13 +352,21 @@ void isa16_3c505_device::map_io(address_map &map) map(0x0180, 0x018f).lr16("mac", [](offs_t offset) { - // TODO: hard-code to Apollo Computer Inc. dummy address for now - static const u8 mac[] = { 0x08, 0x00, 0x1e, 0x12, 0x34, 0x56, 0xff, 0xff}; + // TODO: hard-code to 3Com dummy address for now + static const u8 mac[] = { 0x02, 0x60, 0x8c, 0x12, 0x34, 0x56, 0xff, 0xff}; return mac[offset]; }); } +void isa16_3c505_device::map_isa(address_map &map) +{ + map(0, 0).rw(FUNC(isa16_3c505_device::hcmd_r), FUNC(isa16_3c505_device::hcmd_w)); + map(2, 2).rw(FUNC(isa16_3c505_device::hsr_r), FUNC(isa16_3c505_device::hdr_w)); + map(4, 5).rw(FUNC(isa16_3c505_device::hdata_r), FUNC(isa16_3c505_device::hdata_w)); + map(6, 6).rw(FUNC(isa16_3c505_device::hcr_r), FUNC(isa16_3c505_device::hcr_w)); +} + u8 isa16_3c505_device::acmd_r() { u8 const data = m_hcmdr; @@ -2010,11 +374,7 @@ u8 isa16_3c505_device::acmd_r() m_asr &= ~ASR_HCRF; m_hsr |= HSR_HCRE; - if (m_cpu_irq_asserted) - { - m_cpu_irq_asserted = false; - m_cpu->int0_w(0); - } + update_cpu_irq(0); return data; } @@ -2029,7 +389,7 @@ void isa16_3c505_device::acmd_w(u8 data) m_acmdr = data; if (m_hcr & HCR_CMDE) - update_irq(1); + update_isa_irq(1); } void isa16_3c505_device::acr_w(u8 data) @@ -2041,16 +401,10 @@ void isa16_3c505_device::acr_w(u8 data) m_hsr = (m_hsr & ~HSR_ASF) | (data & ACR_ASF); if ((data ^ m_acr) & ACR_LED1) - { - LOGMASKED(LOG_REG, "led #1 %s\n", (data & ACR_LED1) ? "on" : "off"); m_led[0] = !!(data & ACR_LED1); - } if ((data ^ m_acr) & ACR_LED2) - { - LOGMASKED(LOG_REG, "led #2 %d\n", (data & ACR_LED2) ? "on" : "off"); m_led[1] = !!(data & ACR_LED2); - } if (!(m_acr & ACR_R586) && (data & ACR_R586)) { @@ -2072,7 +426,7 @@ void isa16_3c505_device::acr_w(u8 data) // loopback is active low if ((m_acr & ACR_LPBK) && !(data & ACR_LPBK)) { - LOGMASKED(LOG_REG, "loopback enabled\n", m_data.queue_length()); + LOGMASKED(LOG_REG, "loopback enabled\n"); // TODO: enable loopback on 8023 } @@ -2082,9 +436,12 @@ void isa16_3c505_device::acr_w(u8 data) u16 isa16_3c505_device::adata_r() { - if (!(m_asr & ASR_DIR) && !m_data.empty()) + if (!(m_asr & ASR_DIR) && m_data.queue_length() > 1) { - u16 const data = m_data.dequeue(); + u16 data = m_data.dequeue(); + data |= u16(m_data.dequeue()) << 8; + + LOGMASKED(LOG_DATA, "adata_r 0x%04x\n", data); update_rdy(m_acr, m_hcr); @@ -2092,60 +449,22 @@ u16 isa16_3c505_device::adata_r() } else fatalerror("%s: adata_r read fifo while %s (%s)\n", - tag(), m_data.empty() ? "empty" : "write-only", machine().describe_context().c_str()); + tag(), (m_asr & ASR_DIR) ? "write-only" : "empty", machine().describe_context().c_str()); } void isa16_3c505_device::adata_w(u16 data) { - if ((m_asr & ASR_DIR) && !m_data.full()) + if ((m_asr & ASR_DIR) && m_data.queue_length() < (FIFO_SIZE - 1)) { - m_data.enqueue(data); + LOGMASKED(LOG_DATA, "adata_w 0x%04x\n", data); + m_data.enqueue(u8(data)); + m_data.enqueue(data >> 8); update_rdy(m_acr, m_hcr); } else fatalerror("%s: adata_w write fifo while %s (%s)\n", - tag(), m_data.full() ? "full" : "read-only", machine().describe_context().c_str()); -} - -READ16_MEMBER(isa16_3c505_device::host_r) -{ - switch (offset) - { - case 0: return hcmd_r(); - case 1: return m_hsr; - case 2: return hdata_r(); - case 3: return m_hcr; - } - - logerror("host_r unknown register %d (%s)\n", offset, machine().describe_context()); - return space.unmap(); -} - -WRITE16_MEMBER(isa16_3c505_device::host_w) -{ - switch (offset) - { - case 0: - hcmd_w(data); - break; - - case 1: - m_hdr = data & HDR_BRST; - break; - - case 2: - hdata_w(data); - break; - - case 3: - hcr_w(data); - break; - - default: - logerror("host_w unknown register %d data 0x%04x (%s)\n", offset, data, machine().describe_context()); - break; - } + tag(), !(m_asr & ASR_DIR) ? "read-only" : "full", machine().describe_context().c_str()); } u8 isa16_3c505_device::hcmd_r() @@ -2155,7 +474,7 @@ u8 isa16_3c505_device::hcmd_r() m_asr |= ASR_ACRE; m_hsr &= ~HSR_ACRF; - update_irq(0); + update_isa_irq(0); return data; } @@ -2169,11 +488,7 @@ void isa16_3c505_device::hcmd_w(u8 data) m_hcmdr = data; - if (!m_cpu_irq_asserted) - { - m_cpu_irq_asserted = true; - m_cpu->int0_w(1); - } + update_cpu_irq(1); } void isa16_3c505_device::hcr_w(u8 data) @@ -2189,9 +504,9 @@ void isa16_3c505_device::hcr_w(u8 data) { if (data & HCR_DIR) { - LOGMASKED(LOG_REG, "transfer from host to adapter\n"); + LOGMASKED(LOG_REG, "transfer from adapter to host\n"); - // transfer from host to adapter + // transfer from adapter to host m_hsr |= HSR_DIR; m_asr |= ASR_DIR; @@ -2199,9 +514,9 @@ void isa16_3c505_device::hcr_w(u8 data) } else { - LOGMASKED(LOG_REG, "transfer from adapter to host\n"); + LOGMASKED(LOG_REG, "transfer from host to adapter\n"); - // transfer from adapter to host + // transfer from host to adapter m_hsr &= ~HSR_DIR; m_asr &= ~ASR_DIR; @@ -2209,6 +524,9 @@ void isa16_3c505_device::hcr_w(u8 data) } } + if (!(data & HCR_DMAE)) + m_hsr &= ~HSR_DONE; + if ((data ^ m_hcr) & HCR_FLSH) { if (data & HCR_FLSH) @@ -2243,32 +561,49 @@ void isa16_3c505_device::hcr_w(u8 data) m_hcr = data; } -u16 isa16_3c505_device::hdata_r() +u16 isa16_3c505_device::hdata_r(offs_t offset, u16 mem_mask) { - if ((m_hsr & HSR_DIR) && !m_data.empty()) + unsigned const word = (mem_mask == 0xffff); + u16 data = 0; + + if ((m_hsr & HSR_DIR) && (m_data.queue_length() > word)) { - u16 const data = m_data.dequeue(); + if (ACCESSING_BITS_0_7) + data |= m_data.dequeue(); + + if (ACCESSING_BITS_8_15) + data |= u16(m_data.dequeue()) << 8; + + LOGMASKED(LOG_DATA, "hdata_r 0x%04x mem_mask 0x%04x (%s)\n", data, mem_mask, machine().describe_context()); update_rdy(m_acr, m_hcr); - - return data; } else - fatalerror("%s: hdata_r read fifo while %s (%s)\n", - tag(), m_data.empty() ? "empty" : "write-only", machine().describe_context().c_str()); + logerror("hdata_r read fifo while %s (%s)\n", + !(m_hsr & HSR_DIR) ? "write-only" : "empty", machine().describe_context()); + + return data; } -void isa16_3c505_device::hdata_w(u16 data) +void isa16_3c505_device::hdata_w(offs_t offset, u16 data, u16 mem_mask) { - if (!(m_hsr & HSR_DIR) && !m_data.full()) + unsigned const word = (mem_mask == 0xffff); + + if (!(m_hsr & HSR_DIR) && (m_data.queue_length() < (FIFO_SIZE - word))) { - m_data.enqueue(data); + if (ACCESSING_BITS_0_7) + m_data.enqueue(u8(data)); + + if (ACCESSING_BITS_8_15) + m_data.enqueue(data >> 8); + + LOGMASKED(LOG_DATA, "hdata_w 0x%04x mem_mask 0x%04x (%s)\n", data, mem_mask, machine().describe_context()); update_rdy(m_acr, m_hcr); } else - fatalerror("%s: hdata_w write fifo while %s (%s)\n", - tag(), m_data.full() ? "full" : "read-only", machine().describe_context().c_str()); + logerror("hdata_w write fifo while %s (%s)\n", + (m_hsr & HSR_DIR) ? "read-only" : "full", machine().describe_context()); } void isa16_3c505_device::update_rdy(u8 const acr, u8 const hcr) @@ -2283,7 +618,7 @@ void isa16_3c505_device::update_rdy(u8 const acr, u8 const hcr) else m_hsr |= HSR_HRDY; - if (m_data.full()) + if (m_data.queue_length() > (FIFO_SIZE - 2)) m_asr &= ~ASR_ARDY; else m_asr |= ASR_ARDY; @@ -2291,7 +626,7 @@ void isa16_3c505_device::update_rdy(u8 const acr, u8 const hcr) else { // host to adapter - if (m_data.empty()) + if (m_data.queue_length() < 2) m_asr &= ~ASR_ARDY; else m_asr |= ASR_ARDY; @@ -2307,13 +642,56 @@ void isa16_3c505_device::update_rdy(u8 const acr, u8 const hcr) m_asr &= ~ASR_ARDY; m_hsr &= ~HSR_HRDY; } + + update_cpu_drq(!!(m_asr & ASR_ARDY)); + update_isa_drq((m_hsr & HSR_HRDY) && (hcr & HCR_DMAE)); } -void isa16_3c505_device::update_irq(int state) +void isa16_3c505_device::update_cpu_drq(int state) +{ + if (bool(state) != m_cpu_drq_asserted) + { + m_cpu_drq_asserted = bool(state); + m_cpu->drq1_w(state); + } +} + +void isa16_3c505_device::update_cpu_irq(int state) +{ + if (bool(state) != m_cpu_irq_asserted) + { + m_cpu_irq_asserted = bool(state); + m_cpu->int0_w(state); + } +} + +void isa16_3c505_device::update_isa_drq(int state) +{ + if (bool(state) != m_isa_drq_asserted) + { + LOG("update_isa_drq %d\n", state); + + switch (m_isa_drq) + { + case 1: m_isa->drq1_w(state); break; + case 3: m_isa->drq3_w(state); break; + case 5: m_isa->drq5_w(state); break; + case 6: m_isa->drq6_w(state); break; + case 7: m_isa->drq7_w(state); break; + + default: + fatalerror("%s: invalid isa drq %d\n", tag(), m_isa_drq); + } + + m_isa_drq_asserted = bool(state); + } +} + +void isa16_3c505_device::update_isa_irq(int state) { if (bool(state) != m_isa_irq_asserted) { - LOG("isa irq %d\n", state); + LOG("update_isa_irq %d\n", state); switch (m_isa_irq) { @@ -2336,3 +714,18 @@ void isa16_3c505_device::update_irq(int state) m_isa_irq_asserted = bool(state); } } + +void isa16_3c505_device::eop_w(int state) +{ + LOG("eop_w %d fifo %d\n", state, m_data.queue_length()); + + if (state) + { + m_hsr |= HSR_DONE; + + update_isa_drq(0); + + if (m_hcr & HCR_TCEN) + update_isa_irq(1); + } +} diff --git a/src/devices/bus/isa/3c505.h b/src/devices/bus/isa/3c505.h index afb09cc32f2..115c0c9978e 100644 --- a/src/devices/bus/isa/3c505.h +++ b/src/devices/bus/isa/3c505.h @@ -1,12 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Hans Ostermeyer,R. Belmont -/* - * threecom3c505.h - 3COM 3C505 ethernet controller - * - * Created on: August 27, 2010 - * Author: Hans Ostermeyer - * - */ +// copyright-holders:Patrick Mackinlay #ifndef MAME_BUS_ISA_3C505_H #define MAME_BUS_ISA_3C505_H @@ -19,267 +12,6 @@ #include "bus/isa/isa.h" -// ======================> PCB data structure - -#pragma pack(1) - -struct Memconf -{ - uint16_t cmd_q, rcv_q, mcast, frame, rcv_b, progs; -}; - -struct Rcv_pkt -{ - uint16_t buf_ofs, buf_seg, buf_len, timeout; -}; - -struct Xmit_pkt -{ - uint16_t buf_ofs, buf_seg, pkt_len; -}; - -struct Rcv_resp -{ - uint16_t buf_ofs, buf_seg, buf_len, pkt_len, timeout, status; - uint32_t timetag; -}; - -struct Xmit_resp -{ - uint16_t buf_ofs, buf_seg, c_stat, status; -}; - -struct Netstat -{ - uint32_t tot_recv, tot_xmit; - uint16_t err_CRC, err_align, err_res, err_ovrrun; -}; - -struct Selftest -{ - uint16_t error; - union - { - uint16_t ROM_cksum; - struct - { - uint16_t ofs, seg; - } RAM; - uint16_t i82586; - } failure; -}; - -struct Info -{ - uint8_t minor_vers, major_vers; - uint16_t ROM_cksum, RAM_sz, free_ofs, free_seg; -}; - -struct Memdump -{ - uint16_t size, off, seg; -}; - -/* - Primary Command Block. The most important data structure. All communication - between the host and the adapter is done with these. (Except for the actual - Ethernet data, which has different packaging.) - */ -struct pcb_struct -{ - uint8_t command; - uint8_t length; - union - { - struct Memconf memconf; - uint16_t configure; - struct Rcv_pkt rcv_pkt; - struct Xmit_pkt xmit_pkt; - uint8_t multicast[10][6]; - uint8_t eth_addr[6]; - int16_t failed; - struct Rcv_resp rcv_resp; - struct Xmit_resp xmit_resp; - struct Netstat netstat; - struct Selftest selftest; - struct Info info; - struct Memdump memdump; - uint8_t raw[62]; - } data; -}; - -#pragma pack() - -// ======================> threecom3c505_device - -class threecom3c505_device: public device_t, - public device_network_interface, - public device_isa16_card_interface -{ -public: - // construction/destruction - threecom3c505_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - - // device register I/O - virtual DECLARE_READ16_MEMBER(read); - virtual DECLARE_WRITE16_MEMBER(write); - - static void set_verbose(int on_off); - - virtual void recv_cb(uint8_t *data, int length) override; - -protected: - static constexpr unsigned CMD_BUFFER_SIZE = 100; - static constexpr unsigned ETH_BUFFER_SIZE = 2048; - static constexpr unsigned PGM_BUFFER_SIZE = 0x2000; - - static constexpr unsigned ETHERNET_ADDR_SIZE = 6; - - static constexpr unsigned RX_FIFO_SIZE = 32; - - threecom3c505_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); - - virtual int tx_data(device_t *, const uint8_t *, int); - virtual int setfilter(device_t *, int); - - std::string cpu_context() const; - template void logerror(Format &&fmt, Params &&... args) const; - - // device-level overrides - virtual void device_start() override; - virtual const tiny_rom_entry *device_rom_region() const override; - - required_ioport m_iobase; - required_ioport m_irqdrq; - required_ioport m_romopts; - -private: - // device-level overrides - virtual void device_reset() override; - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; - virtual ioport_constructor device_input_ports() const override; - - class data_buffer_fifo; - - /* data buffer */ - class data_buffer - { - friend class data_buffer_fifo; - - public: - data_buffer(); - void start(threecom3c505_device *device, int32_t size); - void reset(); - int append(uint8_t data); - uint8_t get(int i) { return m_data[i]; }; - uint16_t get_word(int i) { return (m_data[i*2+1] << 8) + m_data[i*2]; }; - int is_empty() {return m_length == 0; }; - int is_full() {return m_length >= m_data.size(); }; - uint16_t get_length() { return m_length; }; - uint16_t get_size() { return m_data.size(); }; - uint8_t *get_data() { return &m_data[0]; }; - void copy(data_buffer *db) const; - void log(const char *title) const; - - private: - std::string cpu_context() const { return m_device->cpu_context(); } - - threecom3c505_device *m_device; // pointer back to our device - uint16_t m_length; - std::vector m_data; - }; - - /* data_buffer fifo (used to buffer the received data) */ - class data_buffer_fifo - { - public: - data_buffer_fifo(); - ~data_buffer_fifo(); - void start(threecom3c505_device *device, int32_t size, int32_t db_size); - void reset(); - int put(const uint8_t data[], const int length); - int get(data_buffer *db); - int is_empty () { return m_get_index == m_put_index; } - int is_full () { return ((m_put_index + 1) % m_size) == m_get_index; } - private: - std::string cpu_context() const { return m_device->cpu_context(); } - - threecom3c505_device *m_device; // pointer back to our device - uint16_t m_size; - uint16_t m_count; - uint16_t m_get_index; - uint16_t m_put_index; - data_buffer *m_db[RX_FIFO_SIZE]; - }; - - void set_filter_list(); - void set_interrupt(enum line_state state); - - void log_command(); - void log_response(); - - void do_receive_command(); - void set_command_pending(int onoff); - - int ethernet_packet_is_for_me(const uint8_t mac_address[]); - - void write_command_port( uint8_t data); - uint8_t read_command_port(); - void write_data_port( uint8_t data); - uint8_t read_data_port(); - void write_control_port( uint8_t data); - uint8_t read_status_port(); - - void do_command(); - - uint8_t m_reg[16]; - - uint8_t m_status; - uint8_t m_control; - - uint8_t m_command_buffer[CMD_BUFFER_SIZE]; - int m_command_index; - int m_command_pending; - int m_wait_for_ack; - int m_wait_for_nak; - - data_buffer_fifo m_rx_fifo; - - data_buffer m_rx_data_buffer; // the ethernet receive buffer - int m_rx_data_index; - int m_rx_pending; - - data_buffer m_tx_data_buffer; // the ethernet transmit buffer - int m_tx_data_length; - - data_buffer m_program_buffer; // the program data buffer - int m_program_length; - - pcb_struct m_response; - int m_response_length; - int m_response_index; - - pcb_struct m_rcv_response; - - uint16_t m_microcode_version; - uint16_t m_microcode_running; - - uint16_t m_i82586_config; - - struct Netstat m_netstat; - - uint8_t m_station_address[ETHERNET_ADDR_SIZE]; - uint8_t m_multicast_list[ETHERNET_ADDR_SIZE*2]; - uint8_t m_filter_list[ETHERNET_ADDR_SIZE*4]; - - enum line_state irq_state; - - emu_timer * m_do_command_timer; // timer to delay command execution - - bool m_installed; - int m_irq, m_drq; -}; - class isa16_3c505_device : public device_t , public device_isa16_card_interface @@ -287,9 +19,6 @@ class isa16_3c505_device public: isa16_3c505_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); - DECLARE_READ16_MEMBER(host_r); - DECLARE_WRITE16_MEMBER(host_w); - protected: // device_t overrides virtual const tiny_rom_entry *device_rom_region() const override; @@ -300,6 +29,7 @@ protected: void map_main(address_map &map); void map_io(address_map &map); + void map_isa(address_map &map); enum acr_mask : u8 { @@ -369,14 +99,27 @@ protected: // host register helpers u8 hcmd_r(); - u16 hdata_r(); + u8 hsr_r() { return m_hsr; } + u16 hdata_r(offs_t offset, u16 mem_mask = 0xffff); + u8 hcr_r() { return m_hcr; } void hcmd_w(u8 data); + void hdr_w(u8 data) { m_hdr = data & HDR_BRST; } + void hdata_w(offs_t offset, u16 data, u16 mem_mask = 0xffff); void hcr_w(u8 data); - void hdata_w(u16 data); - void update_irq(int state); + void update_cpu_drq(int state); + void update_cpu_irq(int state); + void update_isa_drq(int state); + void update_isa_irq(int state); void update_rdy(u8 const acr, u8 const hcr); + // dma helpers + virtual u8 dack_r(int line) override { return hdata_r(0, 0x00ff); } + virtual void dack_w(int line, u8 data) override { hdata_w(0, data, 0x00ff); } + virtual void eop_w(int state) override; + virtual u16 dack16_r(int line) override { return hdata_r(0); } + virtual void dack16_w(int line, u16 data) override { hdata_w(0, data); } + private: required_device m_cpu; required_device m_net; @@ -398,18 +141,20 @@ private: u8 m_hsr; // host status register u8 m_hdr; // host aux dma register - util::fifo m_data; + static constexpr unsigned FIFO_SIZE = 20; + util::fifo m_data; + bool m_installed; unsigned m_isa_irq; unsigned m_isa_drq; + bool m_cpu_drq_asserted; bool m_cpu_irq_asserted; + bool m_isa_drq_asserted; bool m_isa_irq_asserted; - bool m_installed; }; // device type definition -DECLARE_DEVICE_TYPE(ISA16_3C505, threecom3c505_device) -DECLARE_DEVICE_TYPE(ISA16_3C505_LLE, isa16_3c505_device) +DECLARE_DEVICE_TYPE(ISA16_3C505, isa16_3c505_device) #endif // MAME_BUS_ISA_3C505_H diff --git a/src/devices/bus/isa/isa_cards.cpp b/src/devices/bus/isa/isa_cards.cpp index 6c06beabb5e..5b0c2315f38 100644 --- a/src/devices/bus/isa/isa_cards.cpp +++ b/src/devices/bus/isa/isa_cards.cpp @@ -176,7 +176,6 @@ void pc_isa16_cards(device_slot_interface &device) device.option_add("gfxultrap", ISA16_SVGA_GFXULTRAPRO); device.option_add("tgui9680",ISA16_SVGA_TGUI9680); device.option_add("3c505", ISA16_3C505); - device.option_add("3c505_lle", ISA16_3C505_LLE); device.option_add("mach64", ISA16_SVGA_MACH64); device.option_add("sb16_lle", ISA16_SB16); device.option_add("mcd", ISA16_MCD); diff --git a/src/mame/machine/apollo.cpp b/src/mame/machine/apollo.cpp index 736f86b678f..faeb7e9eca6 100644 --- a/src/mame/machine/apollo.cpp +++ b/src/mame/machine/apollo.cpp @@ -1040,11 +1040,20 @@ void apollo_ni::set_node_id_from_disk() #undef VERBOSE #define VERBOSE 0 +DEVICE_INPUT_DEFAULTS_START(3c505) + DEVICE_INPUT_DEFAULTS("IO_BASE", 0x3f0, 0x300) // I/O address 0x300 + DEVICE_INPUT_DEFAULTS("IRQ_DRQ", 0x0f, 0x0a) // IRQ 10 + DEVICE_INPUT_DEFAULTS("IRQ_DRQ", 0x70, 0x60) // DRQ 6 + DEVICE_INPUT_DEFAULTS("ROM_OPTS", 0x01, 0x01) // host ROM enabled + DEVICE_INPUT_DEFAULTS("ROM_OPTS", 0xfe, 0x00) // host ROM address 0x00000 +DEVICE_INPUT_DEFAULTS_END + static void apollo_isa_cards(device_slot_interface &device) { device.option_add("wdc", ISA16_OMTI8621_APOLLO); // Combo ESDI/AT floppy controller device.option_add("ctape", ISA8_SC499); // Archive SC499 cartridge tape - device.option_add("3c505", ISA16_3C505); // 3Com 3C505 Ethernet card + device.option_add("3c505", ISA16_3C505).default_bios("apollo"); // 3Com 3C505 Ethernet card + device.set_option_device_input_defaults("3c505", DEVICE_INPUT_DEFAULTS_NAME(3c505)); } void apollo_state::common(machine_config &config)