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)); if (m_rx_data_buffer.get_length() == 0) { m_rx_fifo.get(&m_rx_data_buffer); } if (!m_command_pending && m_rx_pending > 0 && m_rx_data_buffer.get_length() > 0) { m_rx_pending--; set_command_pending(1); do_receive_command(); } } }
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; }
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); } } } }
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(0); m_wait_for_ack = 1; } } #if 0 else if (m_rx_data_index == data_length && (data_length & 1)) { // data length is odd, pad with 0 to next even size data = 0; m_rx_data_index++; LOG(("!!!: padding 3C505 Rx Data with odd data length %2x with value %02x", data_length, data)); } #endif else { // FIXME: should never happen data = 0xff; m_status &= ~HRDY; /* data register no longer ready */ set_command_pending(0); LOG(("UNEXPECTED: reading 3C505 data Register as %02x (data_length=%02x)", data, data_length)); } return data; }
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 */ }
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); } }
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; }
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 != NULL && // (*tx_data)(this, m_tx_data_buffer.get_data(), m_tx_data_buffer.get_length()) == 0) { // 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; }
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_control & HSF_PCB_MASK) != HSF_PCB_END) { m_command_buffer[m_command_index++] = data; m_status |= HCRE; /* command register empty */ if (m_command_index == 1) { switch (data) { case 0: // spurious data; set command register empty m_command_index = 0; m_status |= HCRE; break; case CMD_MC_F9: // case CMD_MC_FA: // read data length from data port (not from command port) m_tx_data_buffer.reset(); m_status |= HRDY; /* data register ready */ set_command_pending(1); break; default: set_command_pending(1); break; } } } 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; default: do_command(); break; } } }
void threecom3c505_device::do_command() { // default to successful completion m_response.command = m_command_buffer[0] + CMD_RESPONSE_OFFSET; m_response.length = 1; m_response.data.failed = 0; // successful completion switch (m_command_buffer[0]) { case CMD_RESET: // 0x00 // FIXME: // device_reset(); break; case CMD_CONFIGURE_ADAPTER_MEMORY: // 0x01 m_i82586_config = m_program_buffer.get_word(1); break; case CMD_RECEIVE_PACKET: // 0x08 if (m_rx_data_buffer.get_length() == 0 && !m_rx_fifo.get(&m_rx_data_buffer)) { // receive data not yet available m_rx_pending++; set_command_pending(0); } else { do_receive_command(); } return; // break; case CMD_MC_F9: m_response.command = CMD_TRANSMIT_PACKET_COMPLETE; // fall through case CMD_TRANSMIT_PACKET: // 0x09 case CMD_TRANSMIT_PACKET_18: // 0x18 m_response.length = 8; 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; m_microcode_running = 1; m_microcode_version = m_program_buffer.get_word(1); break; case CMD_NETWORK_STATISTICS: // 0x0a m_response.length = sizeof(struct Netstat); // response data must be LSB first! m_response.data.netstat.tot_recv = lsb_first(m_netstat.tot_recv); m_response.data.netstat.tot_xmit = lsb_first(m_netstat.tot_xmit); m_response.data.netstat.err_CRC = lsb_first(m_netstat.err_CRC); m_response.data.netstat.err_align = lsb_first(m_netstat.err_align); m_response.data.netstat.err_res = lsb_first(m_netstat.err_res); m_response.data.netstat.err_ovrrun = lsb_first(m_netstat.err_ovrrun); break; case CMD_ADAPTER_INFO: // 0x11 m_response.length = sizeof(struct Info); // FIXME: replace test data; must be LSB first! m_response.data.info.minor_vers = 1; m_response.data.info.major_vers = 2; m_response.data.info.ROM_cksum = lsb_first(3); m_response.data.info.RAM_sz = lsb_first(4); m_response.data.info.free_ofs = lsb_first(5); m_response.data.info.free_seg = lsb_first(6); break; case CMD_LOAD_MULTICAST_LIST:// 0x0b if (m_command_buffer[1] > sizeof(m_multicast_list) || (m_command_buffer[1] % ETHERNET_ADDR_SIZE) != 0) { LOG(("CMD_LOAD_MULTICAST_LIST - !!! unexpected data size %d !!!", m_command_buffer[1])); } else { memset(m_multicast_list, 0 , sizeof(m_multicast_list)); memcpy(m_multicast_list, m_command_buffer+2 , m_command_buffer[1]-2); set_filter_list(); } break; case CMD_SET_STATION_ADDRESS: // 0x10 if (m_command_buffer[1] != sizeof(m_station_address)) { LOG(("CMD_SET_STATION_ADDRESS - !!! unexpected data size %d !!!", m_command_buffer[1])); memset(m_station_address, 0 , sizeof(m_station_address)); } else { memcpy(m_station_address, m_command_buffer+2 , m_command_buffer[1]); } set_filter_list(); set_mac((char *)m_station_address); break; case CMD_CONFIGURE_82586: // 0x02 case CMD_DOWNLOAD_PROGRAM: // 0x0d case CMD_MC_17: // 0x17 default: break; } m_response_index = 0; m_response_length = m_response.length + 2; m_status |= ACRF; /* adapter command register full */ if (m_control & CMDE) { switch (m_command_buffer[0]) { case CMD_NETWORK_STATISTICS: // 0x0a case CMD_DOWNLOAD_PROGRAM: // 0xd case CMD_EXECUTE_PROGRAM: // 0x0e case CMD_ADAPTER_INFO: // 0x11 // interrupt later m_timer->adjust( attotime::from_msec(5)); break; default: // interrupt at once set_interrupt(ASSERT_LINE); break; } } }
void threecom3c505_device::write_control_port( UINT8 data) { // if (m_device->machine->firstcpu->safe_pcbase() == 0x3C4BAD48) verbose = 3; // if (m_device->machine->firstcpu->safe_pcbase() == 0x010464DC) verbose = 3; if (verbose <= 2 && (data & (DMAE | TCEN /*| CMDE*/)) != 0) { // dma or interrupts enabled LOG1(("writing 3C505 Control Register at offset %02x = %02x", PORT_CONTROL, data)); } 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_NAK: // HSF2 if (m_microcode_running) { m_status = (m_status & ~ASF_PCB_MASK) | ASF_PCB_ACK; } break; case HSF_PCB_END: // (HSF2|HSF1) m_status &= ~(ATTN | DMAE | ASF_PCB_MASK); if (m_microcode_running) { m_status = (m_status & ~ASF_PCB_MASK) | ASF_PCB_ACK; } break; default: // 0 m_command_index = 0; m_status |= HCRE; /* command register empty */ if (m_command_buffer[0] == CMD_MC_F9) { m_mc_f9_pending = 1; //- verbose = 2; } if (data == 0x00) { // hardware reset m_status |= HRDY; /* data register ready */ // flush data register (reset tx data fifo) m_tx_data_length = 0; // FIXME: Don't do this (but why?) // m_tx_data_buffer.reset(); } else if (data == DIR_) { // why?? dex ether 20 expects HRDY // m_status &= ~HRDY; /* data register not ready */ m_status |= HRDY; /* data register ready */ } else if (data & FLSH) { if (data & DIR_) { m_status &= ~HRDY; /* data register not ready */ } else { // flush data register (reset tx data fifo) // m_tx_data_length = 0; m_tx_data_buffer.reset(); m_status |= HRDY; /* data register ready */ } } break; } if ((m_control & (ATTN | FLSH)) == (ATTN | FLSH) && (data & (ATTN | FLSH)) == 0) { m_status |= ASF_PCB_END; } if (m_mc_f9_pending == 2) { m_mc_f9_pending = 0; set_command_pending(0); } m_control = data; }