1880 lines
60 KiB
C++
1880 lines
60 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id: ne2k.cc,v 1.104 2009-02-07 21:05:31 sshwarts Exp $
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2002 MandrakeSoft S.A.
|
|
//
|
|
// MandrakeSoft S.A.
|
|
// 43, rue d'Aboukir
|
|
// 75002 Paris - France
|
|
// http://www.linux-mandrake.com/
|
|
// http://www.mandrakesoft.com/
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// Peter Grehan (grehan@iprg.nokia.com) coded the initial version of this
|
|
// NE2000/ether stuff.
|
|
|
|
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
|
|
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
|
|
// is used to know when we are exporting symbols and when we are importing.
|
|
#define BX_PLUGGABLE
|
|
|
|
#include "iodev.h"
|
|
|
|
#if BX_SUPPORT_NE2K
|
|
|
|
#if BX_SUPPORT_PCI
|
|
#include "pci.h"
|
|
#endif
|
|
#include "ne2k.h"
|
|
#include "eth.h"
|
|
|
|
//Never completely fill the ne2k ring so that we never
|
|
// hit the unclear completely full buffer condition.
|
|
#define BX_NE2K_NEVER_FULL_RING (1)
|
|
|
|
#define LOG_THIS theNE2kDevice->
|
|
|
|
bx_ne2k_c *theNE2kDevice = NULL;
|
|
|
|
const Bit8u ne2k_iomask[32] = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
|
|
|
|
int libne2k_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
|
|
{
|
|
theNE2kDevice = new bx_ne2k_c();
|
|
bx_devices.pluginNE2kDevice = theNE2kDevice;
|
|
BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theNE2kDevice, BX_PLUGIN_NE2K);
|
|
return(0); // Success
|
|
}
|
|
|
|
void libne2k_LTX_plugin_fini(void)
|
|
{
|
|
delete theNE2kDevice;
|
|
}
|
|
|
|
bx_ne2k_c::bx_ne2k_c()
|
|
{
|
|
put("NE2K");
|
|
s.tx_timer_index = BX_NULL_TIMER_HANDLE;
|
|
ethdev = NULL;
|
|
}
|
|
|
|
|
|
bx_ne2k_c::~bx_ne2k_c()
|
|
{
|
|
if (ethdev != NULL) {
|
|
delete ethdev;
|
|
}
|
|
BX_DEBUG(("Exit"));
|
|
}
|
|
|
|
//
|
|
// reset - restore state to power-up, cancelling all i/o
|
|
//
|
|
void bx_ne2k_c::reset(unsigned type)
|
|
{
|
|
if (type == BX_RESET_HARDWARE) {
|
|
// Zero out registers and memory
|
|
memset(&BX_NE2K_THIS s.CR, 0, sizeof(BX_NE2K_THIS s.CR));
|
|
memset(&BX_NE2K_THIS s.IMR, 0, sizeof(BX_NE2K_THIS s.IMR));
|
|
memset(&BX_NE2K_THIS s.DCR, 0, sizeof(BX_NE2K_THIS s.DCR));
|
|
memset(&BX_NE2K_THIS s.TCR, 0, sizeof(BX_NE2K_THIS s.TCR));
|
|
memset(&BX_NE2K_THIS s.TSR, 0, sizeof(BX_NE2K_THIS s.TSR));
|
|
memset(&BX_NE2K_THIS s.RCR, 0, sizeof(BX_NE2K_THIS s.RCR));
|
|
memset(&BX_NE2K_THIS s.RSR, 0, sizeof(BX_NE2K_THIS s.RSR));
|
|
BX_NE2K_THIS s.local_dma = 0;
|
|
BX_NE2K_THIS s.page_start = 0;
|
|
BX_NE2K_THIS s.page_stop = 0;
|
|
BX_NE2K_THIS s.bound_ptr = 0;
|
|
BX_NE2K_THIS s.tx_page_start = 0;
|
|
BX_NE2K_THIS s.num_coll = 0;
|
|
BX_NE2K_THIS s.tx_bytes = 0;
|
|
BX_NE2K_THIS s.fifo = 0;
|
|
BX_NE2K_THIS s.remote_dma = 0;
|
|
BX_NE2K_THIS s.remote_start = 0;
|
|
BX_NE2K_THIS s.remote_bytes = 0;
|
|
BX_NE2K_THIS s.tallycnt_0 = 0;
|
|
BX_NE2K_THIS s.tallycnt_1 = 0;
|
|
BX_NE2K_THIS s.tallycnt_2 = 0;
|
|
|
|
memset(&BX_NE2K_THIS s.physaddr, 0, sizeof(BX_NE2K_THIS s.physaddr));
|
|
memset(&BX_NE2K_THIS s.mchash, 0, sizeof(BX_NE2K_THIS s.mchash));
|
|
BX_NE2K_THIS s.curr_page = 0;
|
|
|
|
BX_NE2K_THIS s.rempkt_ptr = 0;
|
|
BX_NE2K_THIS s.localpkt_ptr = 0;
|
|
BX_NE2K_THIS s.address_cnt = 0;
|
|
|
|
memset(&BX_NE2K_THIS s.mem, 0, sizeof(BX_NE2K_THIS s.mem));
|
|
|
|
// Set power-up conditions
|
|
BX_NE2K_THIS s.CR.stop = 1;
|
|
BX_NE2K_THIS s.CR.rdma_cmd = 4;
|
|
BX_NE2K_THIS s.DCR.longaddr = 1;
|
|
|
|
set_irq_level(0);
|
|
}
|
|
memset(&BX_NE2K_THIS s.ISR, 0, sizeof(BX_NE2K_THIS s.ISR));
|
|
BX_NE2K_THIS s.ISR.reset = 1;
|
|
}
|
|
|
|
void bx_ne2k_c::register_state(void)
|
|
{
|
|
unsigned i;
|
|
char name[6];
|
|
|
|
bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "ne2k", "NE2000 State", 31);
|
|
bx_list_c *CR = new bx_list_c(list, "CR", 5);
|
|
new bx_shadow_bool_c(CR, "stop", &BX_NE2K_THIS s.CR.stop);
|
|
new bx_shadow_bool_c(CR, "start", &BX_NE2K_THIS s.CR.start);
|
|
new bx_shadow_bool_c(CR, "tx_packet", &BX_NE2K_THIS s.CR.tx_packet);
|
|
new bx_shadow_num_c(CR, "rdma_cmd", &BX_NE2K_THIS s.CR.rdma_cmd);
|
|
new bx_shadow_num_c(CR, "pgsel", &BX_NE2K_THIS s.CR.pgsel);
|
|
bx_list_c *ISR = new bx_list_c(list, "ISR", 8);
|
|
new bx_shadow_bool_c(ISR, "pkt_rx", &BX_NE2K_THIS s.ISR.pkt_rx);
|
|
new bx_shadow_bool_c(ISR, "pkt_tx", &BX_NE2K_THIS s.ISR.pkt_tx);
|
|
new bx_shadow_bool_c(ISR, "rx_err", &BX_NE2K_THIS s.ISR.rx_err);
|
|
new bx_shadow_bool_c(ISR, "tx_err", &BX_NE2K_THIS s.ISR.tx_err);
|
|
new bx_shadow_bool_c(ISR, "overwrite", &BX_NE2K_THIS s.ISR.overwrite);
|
|
new bx_shadow_bool_c(ISR, "cnt_oflow", &BX_NE2K_THIS s.ISR.cnt_oflow);
|
|
new bx_shadow_bool_c(ISR, "rdma_done", &BX_NE2K_THIS s.ISR.rdma_done);
|
|
new bx_shadow_bool_c(ISR, "reset", &BX_NE2K_THIS s.ISR.reset);
|
|
bx_list_c *IMR = new bx_list_c(list, "IMR", 7);
|
|
new bx_shadow_bool_c(IMR, "rx_inte", &BX_NE2K_THIS s.IMR.rx_inte);
|
|
new bx_shadow_bool_c(IMR, "tx_inte", &BX_NE2K_THIS s.IMR.tx_inte);
|
|
new bx_shadow_bool_c(IMR, "rxerr_inte", &BX_NE2K_THIS s.IMR.rxerr_inte);
|
|
new bx_shadow_bool_c(IMR, "txerr_inte", &BX_NE2K_THIS s.IMR.txerr_inte);
|
|
new bx_shadow_bool_c(IMR, "overw_inte", &BX_NE2K_THIS s.IMR.overw_inte);
|
|
new bx_shadow_bool_c(IMR, "cofl_inte", &BX_NE2K_THIS s.IMR.cofl_inte);
|
|
new bx_shadow_bool_c(IMR, "rdma_inte", &BX_NE2K_THIS s.IMR.rdma_inte);
|
|
bx_list_c *DCR = new bx_list_c(list, "DCR", 6);
|
|
new bx_shadow_bool_c(DCR, "wdsize", &BX_NE2K_THIS s.DCR.wdsize);
|
|
new bx_shadow_bool_c(DCR, "endian", &BX_NE2K_THIS s.DCR.endian);
|
|
new bx_shadow_bool_c(DCR, "longaddr", &BX_NE2K_THIS s.DCR.longaddr);
|
|
new bx_shadow_bool_c(DCR, "loop", &BX_NE2K_THIS s.DCR.loop);
|
|
new bx_shadow_bool_c(DCR, "auto_rx", &BX_NE2K_THIS s.DCR.auto_rx);
|
|
new bx_shadow_num_c(DCR, "fifo_size", &BX_NE2K_THIS s.DCR.fifo_size);
|
|
bx_list_c *TCR = new bx_list_c(list, "TCR", 4);
|
|
new bx_shadow_bool_c(TCR, "crc_disable", &BX_NE2K_THIS s.TCR.crc_disable);
|
|
new bx_shadow_num_c(TCR, "loop_cntl", &BX_NE2K_THIS s.TCR.loop_cntl);
|
|
new bx_shadow_bool_c(TCR, "ext_stoptx", &BX_NE2K_THIS s.TCR.ext_stoptx);
|
|
new bx_shadow_bool_c(TCR, "coll_prio", &BX_NE2K_THIS s.TCR.coll_prio);
|
|
bx_list_c *TSR = new bx_list_c(list, "TSR", 7);
|
|
new bx_shadow_bool_c(TSR, "tx_ok", &BX_NE2K_THIS s.TSR.tx_ok);
|
|
new bx_shadow_bool_c(TSR, "collided", &BX_NE2K_THIS s.TSR.collided);
|
|
new bx_shadow_bool_c(TSR, "aborted", &BX_NE2K_THIS s.TSR.aborted);
|
|
new bx_shadow_bool_c(TSR, "no_carrier", &BX_NE2K_THIS s.TSR.no_carrier);
|
|
new bx_shadow_bool_c(TSR, "fifo_ur", &BX_NE2K_THIS s.TSR.fifo_ur);
|
|
new bx_shadow_bool_c(TSR, "cd_hbeat", &BX_NE2K_THIS s.TSR.cd_hbeat);
|
|
new bx_shadow_bool_c(TSR, "ow_coll", &BX_NE2K_THIS s.TSR.ow_coll);
|
|
bx_list_c *RCR = new bx_list_c(list, "RCR", 6);
|
|
new bx_shadow_bool_c(RCR, "errors_ok", &BX_NE2K_THIS s.RCR.errors_ok);
|
|
new bx_shadow_bool_c(RCR, "runts_ok", &BX_NE2K_THIS s.RCR.runts_ok);
|
|
new bx_shadow_bool_c(RCR, "broadcast", &BX_NE2K_THIS s.RCR.broadcast);
|
|
new bx_shadow_bool_c(RCR, "multicast", &BX_NE2K_THIS s.RCR.multicast);
|
|
new bx_shadow_bool_c(RCR, "promisc", &BX_NE2K_THIS s.RCR.promisc);
|
|
new bx_shadow_bool_c(RCR, "monitor", &BX_NE2K_THIS s.RCR.monitor);
|
|
bx_list_c *RSR = new bx_list_c(list, "RSR", 8);
|
|
new bx_shadow_bool_c(RSR, "rx_ok", &BX_NE2K_THIS s.RSR.rx_ok);
|
|
new bx_shadow_bool_c(RSR, "bad_crc", &BX_NE2K_THIS s.RSR.bad_crc);
|
|
new bx_shadow_bool_c(RSR, "bad_falign", &BX_NE2K_THIS s.RSR.bad_falign);
|
|
new bx_shadow_bool_c(RSR, "fifo_or", &BX_NE2K_THIS s.RSR.fifo_or);
|
|
new bx_shadow_bool_c(RSR, "rx_missed", &BX_NE2K_THIS s.RSR.rx_missed);
|
|
new bx_shadow_bool_c(RSR, "rx_mbit", &BX_NE2K_THIS s.RSR.rx_mbit);
|
|
new bx_shadow_bool_c(RSR, "rx_disabled", &BX_NE2K_THIS s.RSR.rx_disabled);
|
|
new bx_shadow_bool_c(RSR, "deferred", &BX_NE2K_THIS s.RSR.deferred);
|
|
new bx_shadow_num_c(list, "local_dma", &BX_NE2K_THIS s.local_dma, BASE_HEX);
|
|
new bx_shadow_num_c(list, "page_start", &BX_NE2K_THIS s.page_start, BASE_HEX);
|
|
new bx_shadow_num_c(list, "page_stop", &BX_NE2K_THIS s.page_stop, BASE_HEX);
|
|
new bx_shadow_num_c(list, "bound_ptr", &BX_NE2K_THIS s.bound_ptr, BASE_HEX);
|
|
new bx_shadow_num_c(list, "tx_page_start", &BX_NE2K_THIS s.tx_page_start, BASE_HEX);
|
|
new bx_shadow_num_c(list, "num_coll", &BX_NE2K_THIS s.num_coll, BASE_HEX);
|
|
new bx_shadow_num_c(list, "tx_bytes", &BX_NE2K_THIS s.tx_bytes, BASE_HEX);
|
|
new bx_shadow_num_c(list, "fifo", &BX_NE2K_THIS s.fifo, BASE_HEX);
|
|
new bx_shadow_num_c(list, "remote_dma", &BX_NE2K_THIS s.remote_dma, BASE_HEX);
|
|
new bx_shadow_num_c(list, "remote_start", &BX_NE2K_THIS s.remote_start, BASE_HEX);
|
|
new bx_shadow_num_c(list, "remote_bytes", &BX_NE2K_THIS s.remote_bytes, BASE_HEX);
|
|
new bx_shadow_num_c(list, "tallycnt_0", &BX_NE2K_THIS s.tallycnt_0, BASE_HEX);
|
|
new bx_shadow_num_c(list, "tallycnt_1", &BX_NE2K_THIS s.tallycnt_1, BASE_HEX);
|
|
new bx_shadow_num_c(list, "tallycnt_2", &BX_NE2K_THIS s.tallycnt_2, BASE_HEX);
|
|
bx_list_c *paddr = new bx_list_c(list, "physaddr", 6);
|
|
for (i=0; i<6; i++) {
|
|
sprintf(name, "0x%02x", i);
|
|
new bx_shadow_num_c(paddr, name, &BX_NE2K_THIS s.physaddr[i], BASE_HEX);
|
|
}
|
|
new bx_shadow_num_c(list, "curr_page", &BX_NE2K_THIS s.curr_page, BASE_HEX);
|
|
bx_list_c *mchash = new bx_list_c(list, "mchash", 8);
|
|
for (i=0; i<8; i++) {
|
|
sprintf(name, "0x%02x", i);
|
|
new bx_shadow_num_c(mchash, name, &BX_NE2K_THIS s.mchash[i], BASE_HEX);
|
|
}
|
|
new bx_shadow_num_c(list, "rempkt_ptr", &BX_NE2K_THIS s.rempkt_ptr, BASE_HEX);
|
|
new bx_shadow_num_c(list, "localpkt_ptr", &BX_NE2K_THIS s.localpkt_ptr, BASE_HEX);
|
|
new bx_shadow_num_c(list, "address_cnt", &BX_NE2K_THIS s.address_cnt, BASE_HEX);
|
|
new bx_shadow_data_c(list, "mem", BX_NE2K_THIS s.mem, BX_NE2K_MEMSIZ);
|
|
new bx_shadow_bool_c(list, "tx_timer_active", &BX_NE2K_THIS s.tx_timer_active);
|
|
#if BX_SUPPORT_PCI
|
|
if (BX_NE2K_THIS s.pci_enabled) {
|
|
register_pci_state(list, BX_NE2K_THIS s.pci_conf);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if BX_SUPPORT_PCI
|
|
void bx_ne2k_c::after_restore_state(void)
|
|
{
|
|
if (BX_NE2K_THIS s.pci_enabled) {
|
|
if (DEV_pci_set_base_io(BX_NE2K_THIS_PTR, read_handler, write_handler,
|
|
&BX_NE2K_THIS s.base_address,
|
|
&BX_NE2K_THIS s.pci_conf[0x10],
|
|
32, &ne2k_iomask[0], "NE2000 PCI NIC")) {
|
|
BX_INFO(("new base address: 0x%04x", BX_NE2K_THIS s.base_address));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// read_cr/write_cr - utility routines for handling reads/writes to
|
|
// the Command Register
|
|
//
|
|
Bit32u bx_ne2k_c::read_cr(void)
|
|
{
|
|
Bit32u val =
|
|
(((BX_NE2K_THIS s.CR.pgsel & 0x03) << 6) |
|
|
((BX_NE2K_THIS s.CR.rdma_cmd & 0x07) << 3) |
|
|
(BX_NE2K_THIS s.CR.tx_packet << 2) |
|
|
(BX_NE2K_THIS s.CR.start << 1) |
|
|
(BX_NE2K_THIS s.CR.stop));
|
|
BX_DEBUG(("read CR returns 0x%02x", val));
|
|
return val;
|
|
}
|
|
|
|
void bx_ne2k_c::write_cr(Bit32u value)
|
|
{
|
|
BX_DEBUG(("wrote 0x%02x to CR", value));
|
|
|
|
// Validate remote-DMA
|
|
if ((value & 0x38) == 0x00) {
|
|
BX_DEBUG(("CR write - invalid rDMA value 0"));
|
|
value |= 0x20; /* dma_cmd == 4 is a safe default */
|
|
}
|
|
|
|
// Check for s/w reset
|
|
if (value & 0x01) {
|
|
BX_NE2K_THIS s.ISR.reset = 1;
|
|
BX_NE2K_THIS s.CR.stop = 1;
|
|
} else {
|
|
BX_NE2K_THIS s.CR.stop = 0;
|
|
}
|
|
|
|
BX_NE2K_THIS s.CR.rdma_cmd = (value & 0x38) >> 3;
|
|
|
|
// If start command issued, the RST bit in the ISR
|
|
// must be cleared
|
|
if ((value & 0x02) && !BX_NE2K_THIS s.CR.start) {
|
|
BX_NE2K_THIS s.ISR.reset = 0;
|
|
}
|
|
|
|
BX_NE2K_THIS s.CR.start = ((value & 0x02) == 0x02);
|
|
BX_NE2K_THIS s.CR.pgsel = (value & 0xc0) >> 6;
|
|
|
|
// Check for send-packet command
|
|
if (BX_NE2K_THIS s.CR.rdma_cmd == 3) {
|
|
// Set up DMA read from receive ring
|
|
BX_NE2K_THIS s.remote_start = BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.bound_ptr * 256;
|
|
BX_NE2K_THIS s.remote_bytes = *((Bit16u*) & BX_NE2K_THIS s.mem[BX_NE2K_THIS s.bound_ptr * 256 + 2 - BX_NE2K_MEMSTART]);
|
|
BX_INFO(("Sending buffer #x%x length %d",
|
|
BX_NE2K_THIS s.remote_start,
|
|
BX_NE2K_THIS s.remote_bytes));
|
|
}
|
|
|
|
// Check for start-tx
|
|
if ((value & 0x04) && BX_NE2K_THIS s.TCR.loop_cntl) {
|
|
if (BX_NE2K_THIS s.TCR.loop_cntl != 1) {
|
|
BX_INFO(("Loop mode %d not supported.", BX_NE2K_THIS s.TCR.loop_cntl));
|
|
} else {
|
|
rx_frame (& BX_NE2K_THIS s.mem[BX_NE2K_THIS s.tx_page_start*256 - BX_NE2K_MEMSTART],
|
|
BX_NE2K_THIS s.tx_bytes);
|
|
}
|
|
} else if (value & 0x04) {
|
|
if (BX_NE2K_THIS s.CR.stop || (!BX_NE2K_THIS s.CR.start && !BX_NE2K_THIS s.pci_enabled)) {
|
|
if (BX_NE2K_THIS s.tx_bytes == 0) /* njh@bandsman.co.uk */
|
|
return; /* Solaris9 probe */
|
|
BX_PANIC(("CR write - tx start, dev in reset"));
|
|
}
|
|
|
|
if (BX_NE2K_THIS s.tx_bytes == 0)
|
|
BX_PANIC(("CR write - tx start, tx bytes == 0"));
|
|
|
|
// Send the packet to the system driver
|
|
BX_NE2K_THIS s.CR.tx_packet = 1;
|
|
BX_NE2K_THIS ethdev->sendpkt(& BX_NE2K_THIS s.mem[BX_NE2K_THIS s.tx_page_start*256 - BX_NE2K_MEMSTART], BX_NE2K_THIS s.tx_bytes);
|
|
|
|
// some more debug
|
|
if (BX_NE2K_THIS s.tx_timer_active)
|
|
BX_ERROR(("CR write, tx timer still active"));
|
|
|
|
// Schedule a timer to trigger a tx-complete interrupt
|
|
// The number of microseconds is the bit-time / 10.
|
|
// The bit-time is the preamble+sfd (64 bits), the
|
|
// inter-frame gap (96 bits), the CRC (4 bytes), and the
|
|
// the number of bits in the frame (s.tx_bytes * 8).
|
|
//
|
|
bx_pc_system.activate_timer(BX_NE2K_THIS s.tx_timer_index,
|
|
(64 + 96 + 4*8 + BX_NE2K_THIS s.tx_bytes*8)/10,
|
|
0); // not continuous
|
|
BX_NE2K_THIS s.tx_timer_active = 1;
|
|
}
|
|
|
|
// Linux probes for an interrupt by setting up a remote-DMA read
|
|
// of 0 bytes with remote-DMA completion interrupts enabled.
|
|
// Detect this here
|
|
if (BX_NE2K_THIS s.CR.rdma_cmd == 0x01 &&
|
|
BX_NE2K_THIS s.CR.start &&
|
|
BX_NE2K_THIS s.remote_bytes == 0) {
|
|
BX_NE2K_THIS s.ISR.rdma_done = 1;
|
|
if (BX_NE2K_THIS s.IMR.rdma_inte) {
|
|
set_irq_level(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// chipmem_read/chipmem_write - access the 64K private RAM.
|
|
// The ne2000 memory is accessed through the data port of
|
|
// the asic (offset 0) after setting up a remote-DMA transfer.
|
|
// Both byte and word accesses are allowed.
|
|
// The first 16 bytes contains the MAC address at even locations,
|
|
// and there is 16K of buffer memory starting at 16K
|
|
//
|
|
Bit32u BX_CPP_AttrRegparmN(2)
|
|
bx_ne2k_c::chipmem_read(Bit32u address, unsigned int io_len)
|
|
{
|
|
Bit32u retval = 0;
|
|
|
|
if ((io_len == 2) && (address & 0x1))
|
|
BX_PANIC(("unaligned chipmem word read"));
|
|
|
|
// ROM'd MAC address
|
|
if ((address >=0) && (address <= 31)) {
|
|
retval = BX_NE2K_THIS s.macaddr[address];
|
|
if ((io_len == 2) || (io_len == 4)) {
|
|
retval |= (BX_NE2K_THIS s.macaddr[address + 1] << 8);
|
|
}
|
|
if (io_len == 4) {
|
|
retval |= (BX_NE2K_THIS s.macaddr[address + 2] << 16);
|
|
retval |= (BX_NE2K_THIS s.macaddr[address + 3] << 24);
|
|
}
|
|
return (retval);
|
|
}
|
|
|
|
if ((address >= BX_NE2K_MEMSTART) && (address < BX_NE2K_MEMEND)) {
|
|
retval = BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART];
|
|
if ((io_len == 2) || (io_len == 4)) {
|
|
retval |= (BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 1] << 8);
|
|
}
|
|
if (io_len == 4) {
|
|
retval |= (BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 2] << 16);
|
|
retval |= (BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 3] << 24);
|
|
}
|
|
return (retval);
|
|
}
|
|
|
|
BX_DEBUG(("out-of-bounds chipmem read, %04X", address));
|
|
|
|
return (0xff);
|
|
}
|
|
|
|
void BX_CPP_AttrRegparmN(3)
|
|
bx_ne2k_c::chipmem_write(Bit32u address, Bit32u value, unsigned io_len)
|
|
{
|
|
if ((io_len == 2) && (address & 0x1))
|
|
BX_PANIC(("unaligned chipmem word write"));
|
|
|
|
if ((address >= BX_NE2K_MEMSTART) && (address < BX_NE2K_MEMEND)) {
|
|
BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART] = value & 0xff;
|
|
if ((io_len == 2) || (io_len == 4)) {
|
|
BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 1] = value >> 8;
|
|
}
|
|
if (io_len == 4) {
|
|
BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 2] = value >> 16;
|
|
BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 3] = value >> 24;
|
|
}
|
|
} else
|
|
BX_DEBUG(("out-of-bounds chipmem write, %04X", address));
|
|
}
|
|
|
|
//
|
|
// asic_read/asic_write - This is the high 16 bytes of i/o space
|
|
// (the lower 16 bytes is for the DS8390). Only two locations
|
|
// are used: offset 0, which is used for data transfer, and
|
|
// offset 0xf, which is used to reset the device.
|
|
// The data transfer port is used to as 'external' DMA to the
|
|
// DS8390. The chip has to have the DMA registers set up, and
|
|
// after that, insw/outsw instructions can be used to move
|
|
// the appropriate number of bytes to/from the device.
|
|
//
|
|
Bit32u BX_CPP_AttrRegparmN(2)
|
|
bx_ne2k_c::asic_read(Bit32u offset, unsigned int io_len)
|
|
{
|
|
Bit32u retval = 0;
|
|
|
|
switch (offset) {
|
|
case 0x0: // Data register
|
|
//
|
|
// A read remote-DMA command must have been issued,
|
|
// and the source-address and length registers must
|
|
// have been initialised.
|
|
//
|
|
if (io_len > BX_NE2K_THIS s.remote_bytes) {
|
|
BX_ERROR(("ne2K: dma read underrun iolen=%d remote_bytes=%d",io_len,BX_NE2K_THIS s.remote_bytes));
|
|
//return 0;
|
|
}
|
|
|
|
//BX_INFO(("ne2k read DMA: addr=%4x remote_bytes=%d",BX_NE2K_THIS s.remote_dma,BX_NE2K_THIS s.remote_bytes));
|
|
retval = chipmem_read(BX_NE2K_THIS s.remote_dma, io_len);
|
|
//
|
|
// The 8390 bumps the address and decreases the byte count
|
|
// by the selected word size after every access, not by
|
|
// the amount of data requested by the host (io_len).
|
|
//
|
|
if (io_len == 4) {
|
|
BX_NE2K_THIS s.remote_dma += io_len;
|
|
} else {
|
|
BX_NE2K_THIS s.remote_dma += (BX_NE2K_THIS s.DCR.wdsize + 1);
|
|
}
|
|
if (BX_NE2K_THIS s.remote_dma == BX_NE2K_THIS s.page_stop << 8) {
|
|
BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.page_start << 8;
|
|
}
|
|
// keep s.remote_bytes from underflowing
|
|
if (BX_NE2K_THIS s.remote_bytes > BX_NE2K_THIS s.DCR.wdsize)
|
|
if (io_len == 4) {
|
|
BX_NE2K_THIS s.remote_bytes -= io_len;
|
|
} else {
|
|
BX_NE2K_THIS s.remote_bytes -= (BX_NE2K_THIS s.DCR.wdsize + 1);
|
|
}
|
|
else
|
|
BX_NE2K_THIS s.remote_bytes = 0;
|
|
|
|
// If all bytes have been written, signal remote-DMA complete
|
|
if (BX_NE2K_THIS s.remote_bytes == 0) {
|
|
BX_NE2K_THIS s.ISR.rdma_done = 1;
|
|
if (BX_NE2K_THIS s.IMR.rdma_inte) {
|
|
set_irq_level(1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0xf: // Reset register
|
|
theNE2kDevice->reset(BX_RESET_SOFTWARE);
|
|
break;
|
|
|
|
default:
|
|
BX_INFO(("asic read invalid address %04x", (unsigned) offset));
|
|
break;
|
|
}
|
|
|
|
return (retval);
|
|
}
|
|
|
|
void bx_ne2k_c::asic_write(Bit32u offset, Bit32u value, unsigned io_len)
|
|
{
|
|
BX_DEBUG(("asic write addr=0x%02x, value=0x%04x", (unsigned) offset, (unsigned) value));
|
|
switch (offset) {
|
|
case 0x0: // Data register - see asic_read for a description
|
|
|
|
if ((io_len > 1) && (BX_NE2K_THIS s.DCR.wdsize == 0)) {
|
|
BX_PANIC(("dma write length %d on byte mode operation", io_len));
|
|
break;
|
|
}
|
|
if (BX_NE2K_THIS s.remote_bytes == 0) {
|
|
BX_ERROR(("ne2K: dma write, byte count 0"));
|
|
}
|
|
|
|
chipmem_write(BX_NE2K_THIS s.remote_dma, value, io_len);
|
|
if (io_len == 4) {
|
|
BX_NE2K_THIS s.remote_dma += io_len;
|
|
} else {
|
|
BX_NE2K_THIS s.remote_dma += (BX_NE2K_THIS s.DCR.wdsize + 1);
|
|
}
|
|
if (BX_NE2K_THIS s.remote_dma == BX_NE2K_THIS s.page_stop << 8) {
|
|
BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.page_start << 8;
|
|
}
|
|
|
|
if (io_len == 4) {
|
|
BX_NE2K_THIS s.remote_bytes -= io_len;
|
|
} else {
|
|
BX_NE2K_THIS s.remote_bytes -= (BX_NE2K_THIS s.DCR.wdsize + 1);
|
|
}
|
|
if (BX_NE2K_THIS s.remote_bytes > BX_NE2K_MEMSIZ)
|
|
BX_NE2K_THIS s.remote_bytes = 0;
|
|
|
|
// If all bytes have been written, signal remote-DMA complete
|
|
if (BX_NE2K_THIS s.remote_bytes == 0) {
|
|
BX_NE2K_THIS s.ISR.rdma_done = 1;
|
|
if (BX_NE2K_THIS s.IMR.rdma_inte) {
|
|
set_irq_level(1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0xf: // Reset register
|
|
// end of reset pulse
|
|
break;
|
|
|
|
default: // this is invalid, but happens under win95 device detection
|
|
BX_INFO(("asic write invalid address %04x, ignoring", (unsigned) offset));
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// page0_read/page0_write - These routines handle reads/writes to
|
|
// the 'zeroth' page of the DS8390 register file
|
|
//
|
|
Bit32u bx_ne2k_c::page0_read(Bit32u offset, unsigned int io_len)
|
|
{
|
|
Bit8u value = 0;
|
|
|
|
if (io_len > 1) {
|
|
BX_ERROR(("bad length! page 0 read from register 0x%02x, len=%u", offset,
|
|
io_len)); /* encountered with win98 hardware probe */
|
|
return value;
|
|
}
|
|
|
|
switch (offset) {
|
|
case 0x1: // CLDA0
|
|
value = (BX_NE2K_THIS s.local_dma & 0xff);
|
|
break;
|
|
|
|
case 0x2: // CLDA1
|
|
value = (BX_NE2K_THIS s.local_dma >> 8);
|
|
break;
|
|
|
|
case 0x3: // BNRY
|
|
value = BX_NE2K_THIS s.bound_ptr;
|
|
break;
|
|
|
|
case 0x4: // TSR
|
|
value = ((BX_NE2K_THIS s.TSR.ow_coll << 7) |
|
|
(BX_NE2K_THIS s.TSR.cd_hbeat << 6) |
|
|
(BX_NE2K_THIS s.TSR.fifo_ur << 5) |
|
|
(BX_NE2K_THIS s.TSR.no_carrier << 4) |
|
|
(BX_NE2K_THIS s.TSR.aborted << 3) |
|
|
(BX_NE2K_THIS s.TSR.collided << 2) |
|
|
(BX_NE2K_THIS s.TSR.tx_ok));
|
|
break;
|
|
|
|
case 0x5: // NCR
|
|
value = BX_NE2K_THIS s.num_coll;
|
|
break;
|
|
|
|
case 0x6: // FIFO
|
|
// reading FIFO is only valid in loopback mode
|
|
BX_ERROR(("reading FIFO not supported yet"));
|
|
value = BX_NE2K_THIS s.fifo;
|
|
break;
|
|
|
|
case 0x7: // ISR
|
|
value = ((BX_NE2K_THIS s.ISR.reset << 7) |
|
|
(BX_NE2K_THIS s.ISR.rdma_done << 6) |
|
|
(BX_NE2K_THIS s.ISR.cnt_oflow << 5) |
|
|
(BX_NE2K_THIS s.ISR.overwrite << 4) |
|
|
(BX_NE2K_THIS s.ISR.tx_err << 3) |
|
|
(BX_NE2K_THIS s.ISR.rx_err << 2) |
|
|
(BX_NE2K_THIS s.ISR.pkt_tx << 1) |
|
|
(BX_NE2K_THIS s.ISR.pkt_rx));
|
|
break;
|
|
|
|
case 0x8: // CRDA0
|
|
value = (BX_NE2K_THIS s.remote_dma & 0xff);
|
|
break;
|
|
|
|
case 0x9: // CRDA1
|
|
value = (BX_NE2K_THIS s.remote_dma >> 8);
|
|
break;
|
|
|
|
case 0xa: // reserved / RTL8029ID0
|
|
if (BX_NE2K_THIS s.pci_enabled) {
|
|
value = 0x50;
|
|
} else {
|
|
BX_INFO(("reserved read - page 0, 0xa"));
|
|
value = 0xff;
|
|
}
|
|
break;
|
|
|
|
case 0xb: // reserved / RTL8029ID1
|
|
if (BX_NE2K_THIS s.pci_enabled) {
|
|
value = 0x43;
|
|
} else {
|
|
BX_INFO(("reserved read - page 0, 0xb"));
|
|
value = 0xff;
|
|
}
|
|
break;
|
|
|
|
case 0xc: // RSR
|
|
value = ((BX_NE2K_THIS s.RSR.deferred << 7) |
|
|
(BX_NE2K_THIS s.RSR.rx_disabled << 6) |
|
|
(BX_NE2K_THIS s.RSR.rx_mbit << 5) |
|
|
(BX_NE2K_THIS s.RSR.rx_missed << 4) |
|
|
(BX_NE2K_THIS s.RSR.fifo_or << 3) |
|
|
(BX_NE2K_THIS s.RSR.bad_falign << 2) |
|
|
(BX_NE2K_THIS s.RSR.bad_crc << 1) |
|
|
(BX_NE2K_THIS s.RSR.rx_ok));
|
|
break;
|
|
|
|
case 0xd: // CNTR0
|
|
value = BX_NE2K_THIS s.tallycnt_0;
|
|
break;
|
|
|
|
case 0xe: // CNTR1
|
|
value = BX_NE2K_THIS s.tallycnt_1;
|
|
break;
|
|
|
|
case 0xf: // CNTR2
|
|
value = BX_NE2K_THIS s.tallycnt_2;
|
|
break;
|
|
|
|
default:
|
|
BX_PANIC(("page 0 register 0x%02x out of range", offset));
|
|
}
|
|
|
|
BX_DEBUG(("page 0 read from register 0x%02x, value=0x%02x", offset, value));
|
|
return value;
|
|
}
|
|
|
|
void bx_ne2k_c::page0_write(Bit32u offset, Bit32u value, unsigned io_len)
|
|
{
|
|
Bit8u value2;
|
|
|
|
// It appears to be a common practice to use outw on page0 regs...
|
|
|
|
// break up outw into two outb's
|
|
if (io_len == 2) {
|
|
page0_write(offset, (value & 0xff), 1);
|
|
if (offset < 0x0f) {
|
|
page0_write(offset + 1, ((value >> 8) & 0xff), 1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
BX_DEBUG(("page 0 write to register 0x%02x, value=0x%02x", offset, value));
|
|
|
|
switch (offset) {
|
|
case 0x1: // PSTART
|
|
BX_NE2K_THIS s.page_start = value;
|
|
break;
|
|
|
|
case 0x2: // PSTOP
|
|
BX_NE2K_THIS s.page_stop = value;
|
|
break;
|
|
|
|
case 0x3: // BNRY
|
|
BX_NE2K_THIS s.bound_ptr = value;
|
|
break;
|
|
|
|
case 0x4: // TPSR
|
|
BX_NE2K_THIS s.tx_page_start = value;
|
|
break;
|
|
|
|
case 0x5: // TBCR0
|
|
// Clear out low byte and re-insert
|
|
BX_NE2K_THIS s.tx_bytes &= 0xff00;
|
|
BX_NE2K_THIS s.tx_bytes |= (value & 0xff);
|
|
break;
|
|
|
|
case 0x6: // TBCR1
|
|
// Clear out high byte and re-insert
|
|
BX_NE2K_THIS s.tx_bytes &= 0x00ff;
|
|
BX_NE2K_THIS s.tx_bytes |= ((value & 0xff) << 8);
|
|
break;
|
|
|
|
case 0x7: // ISR
|
|
value &= 0x7f; // clear RST bit - status-only bit
|
|
// All other values are cleared iff the ISR bit is 1
|
|
BX_NE2K_THIS s.ISR.pkt_rx &= ~((bx_bool)((value & 0x01) == 0x01));
|
|
BX_NE2K_THIS s.ISR.pkt_tx &= ~((bx_bool)((value & 0x02) == 0x02));
|
|
BX_NE2K_THIS s.ISR.rx_err &= ~((bx_bool)((value & 0x04) == 0x04));
|
|
BX_NE2K_THIS s.ISR.tx_err &= ~((bx_bool)((value & 0x08) == 0x08));
|
|
BX_NE2K_THIS s.ISR.overwrite &= ~((bx_bool)((value & 0x10) == 0x10));
|
|
BX_NE2K_THIS s.ISR.cnt_oflow &= ~((bx_bool)((value & 0x20) == 0x20));
|
|
BX_NE2K_THIS s.ISR.rdma_done &= ~((bx_bool)((value & 0x40) == 0x40));
|
|
value = ((BX_NE2K_THIS s.ISR.rdma_done << 6) |
|
|
(BX_NE2K_THIS s.ISR.cnt_oflow << 5) |
|
|
(BX_NE2K_THIS s.ISR.overwrite << 4) |
|
|
(BX_NE2K_THIS s.ISR.tx_err << 3) |
|
|
(BX_NE2K_THIS s.ISR.rx_err << 2) |
|
|
(BX_NE2K_THIS s.ISR.pkt_tx << 1) |
|
|
(BX_NE2K_THIS s.ISR.pkt_rx));
|
|
value &= ((BX_NE2K_THIS s.IMR.rdma_inte << 6) |
|
|
(BX_NE2K_THIS s.IMR.cofl_inte << 5) |
|
|
(BX_NE2K_THIS s.IMR.overw_inte << 4) |
|
|
(BX_NE2K_THIS s.IMR.txerr_inte << 3) |
|
|
(BX_NE2K_THIS s.IMR.rxerr_inte << 2) |
|
|
(BX_NE2K_THIS s.IMR.tx_inte << 1) |
|
|
(BX_NE2K_THIS s.IMR.rx_inte));
|
|
if (value == 0)
|
|
set_irq_level(0);
|
|
break;
|
|
|
|
case 0x8: // RSAR0
|
|
// Clear out low byte and re-insert
|
|
BX_NE2K_THIS s.remote_start &= 0xff00;
|
|
BX_NE2K_THIS s.remote_start |= (value & 0xff);
|
|
BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.remote_start;
|
|
break;
|
|
|
|
case 0x9: // RSAR1
|
|
// Clear out high byte and re-insert
|
|
BX_NE2K_THIS s.remote_start &= 0x00ff;
|
|
BX_NE2K_THIS s.remote_start |= ((value & 0xff) << 8);
|
|
BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.remote_start;
|
|
break;
|
|
|
|
case 0xa: // RBCR0
|
|
// Clear out low byte and re-insert
|
|
BX_NE2K_THIS s.remote_bytes &= 0xff00;
|
|
BX_NE2K_THIS s.remote_bytes |= (value & 0xff);
|
|
break;
|
|
|
|
case 0xb: // RBCR1
|
|
// Clear out high byte and re-insert
|
|
BX_NE2K_THIS s.remote_bytes &= 0x00ff;
|
|
BX_NE2K_THIS s.remote_bytes |= ((value & 0xff) << 8);
|
|
break;
|
|
|
|
case 0xc: // RCR
|
|
// Check if the reserved bits are set
|
|
if (value & 0xc0)
|
|
BX_INFO(("RCR write, reserved bits set"));
|
|
|
|
// Set all other bit-fields
|
|
BX_NE2K_THIS s.RCR.errors_ok = ((value & 0x01) == 0x01);
|
|
BX_NE2K_THIS s.RCR.runts_ok = ((value & 0x02) == 0x02);
|
|
BX_NE2K_THIS s.RCR.broadcast = ((value & 0x04) == 0x04);
|
|
BX_NE2K_THIS s.RCR.multicast = ((value & 0x08) == 0x08);
|
|
BX_NE2K_THIS s.RCR.promisc = ((value & 0x10) == 0x10);
|
|
BX_NE2K_THIS s.RCR.monitor = ((value & 0x20) == 0x20);
|
|
|
|
// Monitor bit is a little suspicious...
|
|
if (value & 0x20)
|
|
BX_INFO(("RCR write, monitor bit set!"));
|
|
break;
|
|
|
|
case 0xd: // TCR
|
|
// Check reserved bits
|
|
if (value & 0xe0)
|
|
BX_ERROR(("TCR write, reserved bits set"));
|
|
|
|
// Test loop mode (not supported)
|
|
if (value & 0x06) {
|
|
BX_NE2K_THIS s.TCR.loop_cntl = (value & 0x6) >> 1;
|
|
BX_INFO(("TCR write, loop mode %d not supported", BX_NE2K_THIS s.TCR.loop_cntl));
|
|
} else {
|
|
BX_NE2K_THIS s.TCR.loop_cntl = 0;
|
|
}
|
|
|
|
// Inhibit-CRC not supported.
|
|
if (value & 0x01)
|
|
BX_PANIC(("TCR write, inhibit-CRC not supported"));
|
|
|
|
// Auto-transmit disable very suspicious
|
|
if (value & 0x08)
|
|
BX_PANIC(("TCR write, auto transmit disable not supported"));
|
|
|
|
// Allow collision-offset to be set, although not used
|
|
BX_NE2K_THIS s.TCR.coll_prio = ((value & 0x08) == 0x08);
|
|
break;
|
|
|
|
case 0xe: // DCR
|
|
// the loopback mode is not suppported yet
|
|
if (!(value & 0x08)) {
|
|
BX_ERROR(("DCR write, loopback mode selected"));
|
|
}
|
|
// It is questionable to set longaddr and auto_rx, since they
|
|
// aren't supported on the ne2000. Print a warning and continue
|
|
if (value & 0x04)
|
|
BX_INFO(("DCR write - LAS set ???"));
|
|
if (value & 0x10)
|
|
BX_INFO(("DCR write - AR set ???"));
|
|
|
|
// Set other values.
|
|
BX_NE2K_THIS s.DCR.wdsize = ((value & 0x01) == 0x01);
|
|
BX_NE2K_THIS s.DCR.endian = ((value & 0x02) == 0x02);
|
|
BX_NE2K_THIS s.DCR.longaddr = ((value & 0x04) == 0x04); // illegal ?
|
|
BX_NE2K_THIS s.DCR.loop = ((value & 0x08) == 0x08);
|
|
BX_NE2K_THIS s.DCR.auto_rx = ((value & 0x10) == 0x10); // also illegal ?
|
|
BX_NE2K_THIS s.DCR.fifo_size = (value & 0x50) >> 5;
|
|
break;
|
|
|
|
case 0xf: // IMR
|
|
// Check for reserved bit
|
|
if (value & 0x80)
|
|
BX_ERROR(("IMR write, reserved bit set"));
|
|
|
|
// Set other values
|
|
BX_NE2K_THIS s.IMR.rx_inte = ((value & 0x01) == 0x01);
|
|
BX_NE2K_THIS s.IMR.tx_inte = ((value & 0x02) == 0x02);
|
|
BX_NE2K_THIS s.IMR.rxerr_inte = ((value & 0x04) == 0x04);
|
|
BX_NE2K_THIS s.IMR.txerr_inte = ((value & 0x08) == 0x08);
|
|
BX_NE2K_THIS s.IMR.overw_inte = ((value & 0x10) == 0x10);
|
|
BX_NE2K_THIS s.IMR.cofl_inte = ((value & 0x20) == 0x20);
|
|
BX_NE2K_THIS s.IMR.rdma_inte = ((value & 0x40) == 0x40);
|
|
value2 = ((BX_NE2K_THIS s.ISR.rdma_done << 6) |
|
|
(BX_NE2K_THIS s.ISR.cnt_oflow << 5) |
|
|
(BX_NE2K_THIS s.ISR.overwrite << 4) |
|
|
(BX_NE2K_THIS s.ISR.tx_err << 3) |
|
|
(BX_NE2K_THIS s.ISR.rx_err << 2) |
|
|
(BX_NE2K_THIS s.ISR.pkt_tx << 1) |
|
|
(BX_NE2K_THIS s.ISR.pkt_rx));
|
|
if (((value & value2) & 0x7f) == 0) {
|
|
set_irq_level(0);
|
|
} else {
|
|
set_irq_level(1);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
BX_PANIC(("page 0 write, bad register 0x%02x", offset));
|
|
}
|
|
}
|
|
|
|
//
|
|
// page1_read/page1_write - These routines handle reads/writes to
|
|
// the first page of the DS8390 register file
|
|
//
|
|
Bit32u bx_ne2k_c::page1_read(Bit32u offset, unsigned int io_len)
|
|
{
|
|
BX_DEBUG(("page 1 read from register 0x%02x, len=%u", offset, io_len));
|
|
|
|
if (io_len > 1)
|
|
BX_PANIC(("bad length! page 1 read from register 0x%02x, len=%u", offset, io_len));
|
|
|
|
switch (offset) {
|
|
case 0x1: // PAR0-5
|
|
case 0x2:
|
|
case 0x3:
|
|
case 0x4:
|
|
case 0x5:
|
|
case 0x6:
|
|
return (BX_NE2K_THIS s.physaddr[offset - 1]);
|
|
break;
|
|
|
|
case 0x7: // CURR
|
|
BX_DEBUG(("returning current page: 0x%02x", (BX_NE2K_THIS s.curr_page)));
|
|
return (BX_NE2K_THIS s.curr_page);
|
|
|
|
case 0x8: // MAR0-7
|
|
case 0x9:
|
|
case 0xa:
|
|
case 0xb:
|
|
case 0xc:
|
|
case 0xd:
|
|
case 0xe:
|
|
case 0xf:
|
|
return (BX_NE2K_THIS s.mchash[offset - 8]);
|
|
break;
|
|
|
|
default:
|
|
BX_PANIC(("page 1 read register 0x%02x out of range", offset));
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
void bx_ne2k_c::page1_write(Bit32u offset, Bit32u value, unsigned io_len)
|
|
{
|
|
BX_DEBUG(("page 1 write to register 0x%02x, len=%u, value=0x%04x", offset,
|
|
io_len, value));
|
|
|
|
switch (offset) {
|
|
case 0x1: // PAR0-5
|
|
case 0x2:
|
|
case 0x3:
|
|
case 0x4:
|
|
case 0x5:
|
|
case 0x6:
|
|
BX_NE2K_THIS s.physaddr[offset - 1] = value;
|
|
if (offset == 6) {
|
|
BX_INFO(("Physical address set to %02x:%02x:%02x:%02x:%02x:%02x",
|
|
BX_NE2K_THIS s.physaddr[0],
|
|
BX_NE2K_THIS s.physaddr[1],
|
|
BX_NE2K_THIS s.physaddr[2],
|
|
BX_NE2K_THIS s.physaddr[3],
|
|
BX_NE2K_THIS s.physaddr[4],
|
|
BX_NE2K_THIS s.physaddr[5]));
|
|
}
|
|
break;
|
|
|
|
case 0x7: // CURR
|
|
BX_NE2K_THIS s.curr_page = value;
|
|
break;
|
|
|
|
case 0x8: // MAR0-7
|
|
case 0x9:
|
|
case 0xa:
|
|
case 0xb:
|
|
case 0xc:
|
|
case 0xd:
|
|
case 0xe:
|
|
case 0xf:
|
|
BX_NE2K_THIS s.mchash[offset - 8] = value;
|
|
break;
|
|
|
|
default:
|
|
BX_PANIC(("page 1 write register 0x%02x out of range", offset));
|
|
}
|
|
}
|
|
|
|
//
|
|
// page2_read/page2_write - These routines handle reads/writes to
|
|
// the second page of the DS8390 register file
|
|
//
|
|
Bit32u bx_ne2k_c::page2_read(Bit32u offset, unsigned int io_len)
|
|
{
|
|
BX_DEBUG(("page 2 read from register 0x%02x, len=%u", offset, io_len));
|
|
|
|
if (io_len > 1)
|
|
BX_PANIC(("bad length! page 2 read from register 0x%02x, len=%u", offset, io_len));
|
|
|
|
switch (offset) {
|
|
case 0x1: // PSTART
|
|
return (BX_NE2K_THIS s.page_start);
|
|
|
|
case 0x2: // PSTOP
|
|
return (BX_NE2K_THIS s.page_stop);
|
|
|
|
case 0x3: // Remote Next-packet pointer
|
|
return (BX_NE2K_THIS s.rempkt_ptr);
|
|
|
|
case 0x4: // TPSR
|
|
return (BX_NE2K_THIS s.tx_page_start);
|
|
|
|
case 0x5: // Local Next-packet pointer
|
|
return (BX_NE2K_THIS s.localpkt_ptr);
|
|
|
|
case 0x6: // Address counter (upper)
|
|
return (BX_NE2K_THIS s.address_cnt >> 8);
|
|
|
|
case 0x7: // Address counter (lower)
|
|
return (BX_NE2K_THIS s.address_cnt & 0xff);
|
|
|
|
case 0x8: // Reserved
|
|
case 0x9:
|
|
case 0xa:
|
|
case 0xb:
|
|
BX_ERROR(("reserved read - page 2, register 0x%02x", offset));
|
|
return (0xff);
|
|
|
|
case 0xc: // RCR
|
|
return ((BX_NE2K_THIS s.RCR.monitor << 5) |
|
|
(BX_NE2K_THIS s.RCR.promisc << 4) |
|
|
(BX_NE2K_THIS s.RCR.multicast << 3) |
|
|
(BX_NE2K_THIS s.RCR.broadcast << 2) |
|
|
(BX_NE2K_THIS s.RCR.runts_ok << 1) |
|
|
(BX_NE2K_THIS s.RCR.errors_ok));
|
|
|
|
case 0xd: // TCR
|
|
return ((BX_NE2K_THIS s.TCR.coll_prio << 4) |
|
|
(BX_NE2K_THIS s.TCR.ext_stoptx << 3) |
|
|
((BX_NE2K_THIS s.TCR.loop_cntl & 0x3) << 1) |
|
|
(BX_NE2K_THIS s.TCR.crc_disable));
|
|
|
|
case 0xe: // DCR
|
|
return (((BX_NE2K_THIS s.DCR.fifo_size & 0x3) << 5) |
|
|
(BX_NE2K_THIS s.DCR.auto_rx << 4) |
|
|
(BX_NE2K_THIS s.DCR.loop << 3) |
|
|
(BX_NE2K_THIS s.DCR.longaddr << 2) |
|
|
(BX_NE2K_THIS s.DCR.endian << 1) |
|
|
(BX_NE2K_THIS s.DCR.wdsize));
|
|
|
|
case 0xf: // IMR
|
|
return ((BX_NE2K_THIS s.IMR.rdma_inte << 6) |
|
|
(BX_NE2K_THIS s.IMR.cofl_inte << 5) |
|
|
(BX_NE2K_THIS s.IMR.overw_inte << 4) |
|
|
(BX_NE2K_THIS s.IMR.txerr_inte << 3) |
|
|
(BX_NE2K_THIS s.IMR.rxerr_inte << 2) |
|
|
(BX_NE2K_THIS s.IMR.tx_inte << 1) |
|
|
(BX_NE2K_THIS s.IMR.rx_inte));
|
|
|
|
default:
|
|
BX_PANIC(("page 2 register 0x%02x out of range", offset));
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
void bx_ne2k_c::page2_write(Bit32u offset, Bit32u value, unsigned io_len)
|
|
{
|
|
// Maybe all writes here should be BX_PANIC()'d, since they
|
|
// affect internal operation, but let them through for now
|
|
// and print a warning.
|
|
BX_ERROR(("page 2 write to register 0x%02x, len=%u, value=0x%04x", offset,
|
|
io_len, value));
|
|
|
|
switch (offset) {
|
|
case 0x1: // CLDA0
|
|
// Clear out low byte and re-insert
|
|
BX_NE2K_THIS s.local_dma &= 0xff00;
|
|
BX_NE2K_THIS s.local_dma |= (value & 0xff);
|
|
break;
|
|
|
|
case 0x2: // CLDA1
|
|
// Clear out high byte and re-insert
|
|
BX_NE2K_THIS s.local_dma &= 0x00ff;
|
|
BX_NE2K_THIS s.local_dma |= ((value & 0xff) << 8);
|
|
break;
|
|
|
|
case 0x3: // Remote Next-pkt pointer
|
|
BX_NE2K_THIS s.rempkt_ptr = value;
|
|
break;
|
|
|
|
case 0x4:
|
|
BX_PANIC(("page 2 write to reserved register 0x04"));
|
|
break;
|
|
|
|
case 0x5: // Local Next-packet pointer
|
|
BX_NE2K_THIS s.localpkt_ptr = value;
|
|
break;
|
|
|
|
case 0x6: // Address counter (upper)
|
|
// Clear out high byte and re-insert
|
|
BX_NE2K_THIS s.address_cnt &= 0x00ff;
|
|
BX_NE2K_THIS s.address_cnt |= ((value & 0xff) << 8);
|
|
break;
|
|
|
|
case 0x7: // Address counter (lower)
|
|
// Clear out low byte and re-insert
|
|
BX_NE2K_THIS s.address_cnt &= 0xff00;
|
|
BX_NE2K_THIS s.address_cnt |= (value & 0xff);
|
|
break;
|
|
|
|
case 0x8:
|
|
case 0x9:
|
|
case 0xa:
|
|
case 0xb:
|
|
case 0xc:
|
|
case 0xd:
|
|
case 0xe:
|
|
case 0xf:
|
|
BX_PANIC(("page 2 write to reserved register 0x%02x", offset));
|
|
break;
|
|
|
|
default:
|
|
BX_PANIC(("page 2 write, illegal register 0x%02x", offset));
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// page3_read/page3_write - writes to this page are illegal
|
|
//
|
|
Bit32u bx_ne2k_c::page3_read(Bit32u offset, unsigned int io_len)
|
|
{
|
|
if (BX_NE2K_THIS s.pci_enabled) {
|
|
switch (offset) {
|
|
case 0x3: // CONFIG0
|
|
return (0);
|
|
case 0x5: // CONFIG2
|
|
return (0x40);
|
|
case 0x6: // CONFIG3
|
|
return (0x40);
|
|
default:
|
|
BX_ERROR(("page 3 read register 0x%02x attempted", offset));
|
|
return (0);
|
|
}
|
|
} else {
|
|
BX_ERROR(("page 3 read register 0x%02x attempted", offset));
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
void bx_ne2k_c::page3_write(Bit32u offset, Bit32u value, unsigned io_len)
|
|
{
|
|
BX_ERROR(("page 3 write register 0x%02x attempted", offset));
|
|
}
|
|
|
|
//
|
|
// tx_timer_handler/tx_timer
|
|
//
|
|
void bx_ne2k_c::tx_timer_handler(void *this_ptr)
|
|
{
|
|
bx_ne2k_c *class_ptr = (bx_ne2k_c *) this_ptr;
|
|
class_ptr->tx_timer();
|
|
}
|
|
|
|
void bx_ne2k_c::tx_timer(void)
|
|
{
|
|
BX_DEBUG(("tx_timer"));
|
|
BX_NE2K_THIS s.CR.tx_packet = 0;
|
|
BX_NE2K_THIS s.TSR.tx_ok = 1;
|
|
// Generate an interrupt if not masked and not one in progress
|
|
if (BX_NE2K_THIS s.IMR.tx_inte && !BX_NE2K_THIS s.ISR.pkt_tx) {
|
|
BX_NE2K_THIS s.ISR.pkt_tx = 1;
|
|
set_irq_level(1);
|
|
}
|
|
BX_NE2K_THIS s.tx_timer_active = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// read_handler/read - i/o 'catcher' function called from BOCHS
|
|
// mainline when the CPU attempts a read in the i/o space registered
|
|
// by this ne2000 instance
|
|
//
|
|
Bit32u bx_ne2k_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
|
|
{
|
|
#if !BX_USE_NE2K_SMF
|
|
bx_ne2k_c *class_ptr = (bx_ne2k_c *) this_ptr;
|
|
|
|
return class_ptr->read(address, io_len);
|
|
}
|
|
|
|
Bit32u bx_ne2k_c::read(Bit32u address, unsigned io_len)
|
|
{
|
|
#else
|
|
UNUSED(this_ptr);
|
|
#endif // !BX_USE_NE2K_SMF
|
|
BX_DEBUG(("read addr %x, len %d", address, io_len));
|
|
Bit32u retval = 0;
|
|
int offset = address - BX_NE2K_THIS s.base_address;
|
|
|
|
if (offset >= 0x10) {
|
|
retval = asic_read(offset - 0x10, io_len);
|
|
} else if (offset == 0x00) {
|
|
retval = read_cr();
|
|
} else {
|
|
switch (BX_NE2K_THIS s.CR.pgsel) {
|
|
case 0x00:
|
|
retval = page0_read(offset, io_len);
|
|
break;
|
|
|
|
case 0x01:
|
|
retval = page1_read(offset, io_len);
|
|
break;
|
|
|
|
case 0x02:
|
|
retval = page2_read(offset, io_len);
|
|
break;
|
|
|
|
case 0x03:
|
|
retval = page3_read(offset, io_len);
|
|
break;
|
|
|
|
default:
|
|
BX_PANIC(("ne2K: unknown value of pgsel in read - %d",
|
|
BX_NE2K_THIS s.CR.pgsel));
|
|
}
|
|
}
|
|
|
|
return (retval);
|
|
}
|
|
|
|
//
|
|
// write_handler/write - i/o 'catcher' function called from BOCHS
|
|
// mainline when the CPU attempts a write in the i/o space registered
|
|
// by this ne2000 instance
|
|
//
|
|
void bx_ne2k_c::write_handler(void *this_ptr, Bit32u address, Bit32u value,
|
|
unsigned io_len)
|
|
{
|
|
#if !BX_USE_NE2K_SMF
|
|
bx_ne2k_c *class_ptr = (bx_ne2k_c *) this_ptr;
|
|
class_ptr->write(address, value, io_len);
|
|
}
|
|
|
|
void bx_ne2k_c::write(Bit32u address, Bit32u value, unsigned io_len)
|
|
{
|
|
#else
|
|
UNUSED(this_ptr);
|
|
#endif // !BX_USE_NE2K_SMF
|
|
BX_DEBUG(("write addr %x, value %x len %d", address, value, io_len));
|
|
int offset = address - BX_NE2K_THIS s.base_address;
|
|
|
|
//
|
|
// The high 16 bytes of i/o space are for the ne2000 asic -
|
|
// the low 16 bytes are for the DS8390, with the current
|
|
// page being selected by the PS0,PS1 registers in the
|
|
// command register
|
|
//
|
|
if (offset >= 0x10) {
|
|
asic_write(offset - 0x10, value, io_len);
|
|
} else if (offset == 0x00) {
|
|
write_cr(value);
|
|
} else {
|
|
switch (BX_NE2K_THIS s.CR.pgsel) {
|
|
case 0x00:
|
|
page0_write(offset, value, io_len);
|
|
break;
|
|
|
|
case 0x01:
|
|
page1_write(offset, value, io_len);
|
|
break;
|
|
|
|
case 0x02:
|
|
page2_write(offset, value, io_len);
|
|
break;
|
|
|
|
case 0x03:
|
|
page3_write(offset, value, io_len);
|
|
break;
|
|
|
|
default:
|
|
BX_PANIC(("ne2K: unknown value of pgsel in write - %d",
|
|
BX_NE2K_THIS s.CR.pgsel));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* mcast_index() - return the 6-bit index into the multicast
|
|
* table. Stolen unashamedly from FreeBSD's if_ed.c
|
|
*/
|
|
unsigned bx_ne2k_c::mcast_index(const void *dst)
|
|
{
|
|
#define POLYNOMIAL 0x04c11db6
|
|
Bit32u crc = 0xffffffffL;
|
|
int carry, i, j;
|
|
unsigned char b;
|
|
unsigned char *ep = (unsigned char *) dst;
|
|
|
|
for (i = 6; --i >= 0;) {
|
|
b = *ep++;
|
|
for (j = 8; --j >= 0;) {
|
|
carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
|
|
crc <<= 1;
|
|
b >>= 1;
|
|
if (carry)
|
|
crc = ((crc ^ POLYNOMIAL) | carry);
|
|
}
|
|
}
|
|
return (crc >> 26);
|
|
#undef POLYNOMIAL
|
|
}
|
|
|
|
/*
|
|
* Callback from the eth system driver when a frame has arrived
|
|
*/
|
|
void bx_ne2k_c::rx_handler(void *arg, const void *buf, unsigned len)
|
|
{
|
|
// BX_DEBUG(("rx_handler with length %d", len));
|
|
bx_ne2k_c *class_ptr = (bx_ne2k_c *) arg;
|
|
|
|
class_ptr->rx_frame(buf, len);
|
|
}
|
|
|
|
/*
|
|
* rx_frame() - called by the platform-specific code when an
|
|
* ethernet frame has been received. The destination address
|
|
* is tested to see if it should be accepted, and if the
|
|
* rx ring has enough room, it is copied into it and
|
|
* the receive process is updated
|
|
*/
|
|
void bx_ne2k_c::rx_frame(const void *buf, unsigned io_len)
|
|
{
|
|
int pages;
|
|
int avail;
|
|
unsigned idx;
|
|
int wrapped;
|
|
int nextpage;
|
|
unsigned char pkthdr[4];
|
|
unsigned char *pktbuf = (unsigned char *) buf;
|
|
unsigned char *startptr;
|
|
|
|
BX_DEBUG(("rx_frame with length %d", io_len));
|
|
|
|
|
|
if ((BX_NE2K_THIS s.CR.stop != 0) ||
|
|
(BX_NE2K_THIS s.page_start == 0) ||
|
|
((BX_NE2K_THIS s.DCR.loop == 0) &&
|
|
(BX_NE2K_THIS s.TCR.loop_cntl != 0))) {
|
|
|
|
return;
|
|
}
|
|
|
|
// Add the pkt header + CRC to the length, and work
|
|
// out how many 256-byte pages the frame would occupy
|
|
pages = (io_len + 4 + 4 + 255)/256;
|
|
|
|
if (BX_NE2K_THIS s.curr_page < BX_NE2K_THIS s.bound_ptr) {
|
|
avail = BX_NE2K_THIS s.bound_ptr - BX_NE2K_THIS s.curr_page;
|
|
} else {
|
|
avail = (BX_NE2K_THIS s.page_stop - BX_NE2K_THIS s.page_start) -
|
|
(BX_NE2K_THIS s.curr_page - BX_NE2K_THIS s.bound_ptr);
|
|
wrapped = 1;
|
|
}
|
|
|
|
// Avoid getting into a buffer overflow condition by not attempting
|
|
// to do partial receives. The emulation to handle this condition
|
|
// seems particularly painful.
|
|
if ((avail < pages)
|
|
#if BX_NE2K_NEVER_FULL_RING
|
|
|| (avail == pages)
|
|
#endif
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if ((io_len < 60) && !BX_NE2K_THIS s.RCR.runts_ok) {
|
|
BX_DEBUG(("rejected small packet, length %d", io_len));
|
|
return;
|
|
}
|
|
|
|
// Do address filtering if not in promiscuous mode
|
|
if (! BX_NE2K_THIS s.RCR.promisc) {
|
|
if (!memcmp(buf, broadcast_macaddr, 6)) {
|
|
if (!BX_NE2K_THIS s.RCR.broadcast) {
|
|
return;
|
|
}
|
|
} else if (pktbuf[0] & 0x01) {
|
|
if (! BX_NE2K_THIS s.RCR.multicast) {
|
|
return;
|
|
}
|
|
idx = mcast_index(buf);
|
|
if (!(BX_NE2K_THIS s.mchash[idx >> 3] & (1 << (idx & 0x7)))) {
|
|
return;
|
|
}
|
|
} else if (0 != memcmp(buf, BX_NE2K_THIS s.physaddr, 6)) {
|
|
return;
|
|
}
|
|
} else {
|
|
BX_DEBUG(("rx_frame promiscuous receive"));
|
|
}
|
|
|
|
// BX_INFO(("rx_frame %d to %x:%x:%x:%x:%x:%x from %x:%x:%x:%x:%x:%x",
|
|
// io_len,
|
|
// pktbuf[0], pktbuf[1], pktbuf[2], pktbuf[3], pktbuf[4], pktbuf[5],
|
|
// pktbuf[6], pktbuf[7], pktbuf[8], pktbuf[9], pktbuf[10], pktbuf[11]));
|
|
|
|
nextpage = BX_NE2K_THIS s.curr_page + pages;
|
|
if (nextpage >= BX_NE2K_THIS s.page_stop) {
|
|
nextpage -= BX_NE2K_THIS s.page_stop - BX_NE2K_THIS s.page_start;
|
|
}
|
|
|
|
// Setup packet header
|
|
pkthdr[0] = 0; // rx status - old behavior
|
|
pkthdr[0] = 1; // Probably better to set it all the time
|
|
// rather than set it to 0, which is clearly wrong.
|
|
if (pktbuf[0] & 0x01) {
|
|
pkthdr[0] |= 0x20; // rx status += multicast packet
|
|
}
|
|
pkthdr[1] = nextpage; // ptr to next packet
|
|
pkthdr[2] = (io_len + 4) & 0xff; // length-low
|
|
pkthdr[3] = (io_len + 4) >> 8; // length-hi
|
|
|
|
// copy into buffer, update curpage, and signal interrupt if config'd
|
|
startptr = & BX_NE2K_THIS s.mem[BX_NE2K_THIS s.curr_page * 256 -
|
|
BX_NE2K_MEMSTART];
|
|
if ((nextpage > BX_NE2K_THIS s.curr_page) ||
|
|
((BX_NE2K_THIS s.curr_page + pages) == BX_NE2K_THIS s.page_stop)) {
|
|
memcpy(startptr, pkthdr, 4);
|
|
memcpy(startptr + 4, buf, io_len);
|
|
BX_NE2K_THIS s.curr_page = nextpage;
|
|
} else {
|
|
int endbytes = (BX_NE2K_THIS s.page_stop - BX_NE2K_THIS s.curr_page)
|
|
* 256;
|
|
memcpy(startptr, pkthdr, 4);
|
|
memcpy(startptr + 4, buf, endbytes - 4);
|
|
startptr = & BX_NE2K_THIS s.mem[BX_NE2K_THIS s.page_start * 256 -
|
|
BX_NE2K_MEMSTART];
|
|
memcpy(startptr, (void *)(pktbuf + endbytes - 4),
|
|
io_len - endbytes + 8);
|
|
BX_NE2K_THIS s.curr_page = nextpage;
|
|
}
|
|
|
|
BX_NE2K_THIS s.RSR.rx_ok = 1;
|
|
BX_NE2K_THIS s.RSR.rx_mbit = (bx_bool)((pktbuf[0] & 0x01) > 0);
|
|
|
|
BX_NE2K_THIS s.ISR.pkt_rx = 1;
|
|
|
|
if (BX_NE2K_THIS s.IMR.rx_inte) {
|
|
set_irq_level(1);
|
|
}
|
|
|
|
}
|
|
|
|
void bx_ne2k_c::init(void)
|
|
{
|
|
char devname[16];
|
|
Bit8u macaddr[6];
|
|
bx_list_c *base;
|
|
|
|
BX_DEBUG(("Init $Id: ne2k.cc,v 1.104 2009-02-07 21:05:31 sshwarts Exp $"));
|
|
|
|
// Read in values from config interface
|
|
base = (bx_list_c*) SIM->get_param(BXPN_NE2K);
|
|
memcpy(macaddr, SIM->get_param_string("macaddr", base)->getptr(), 6);
|
|
BX_NE2K_THIS s.pci_enabled = 0;
|
|
strcpy(devname, "NE2000 NIC");
|
|
|
|
#if BX_SUPPORT_PCI
|
|
if ((SIM->get_param_bool(BXPN_I440FX_SUPPORT)->get()) &&
|
|
(DEV_is_pci_device(BX_PLUGIN_NE2K))) {
|
|
BX_NE2K_THIS s.pci_enabled = 1;
|
|
strcpy(devname, "NE2000 PCI NIC");
|
|
BX_NE2K_THIS s.devfunc = 0x00;
|
|
DEV_register_pci_handlers(this, &BX_NE2K_THIS s.devfunc,
|
|
BX_PLUGIN_NE2K, devname);
|
|
|
|
for (unsigned i=0; i<256; i++)
|
|
BX_NE2K_THIS s.pci_conf[i] = 0x0;
|
|
// readonly registers
|
|
BX_NE2K_THIS s.pci_conf[0x00] = 0xec;
|
|
BX_NE2K_THIS s.pci_conf[0x01] = 0x10;
|
|
BX_NE2K_THIS s.pci_conf[0x02] = 0x29;
|
|
BX_NE2K_THIS s.pci_conf[0x03] = 0x80;
|
|
BX_NE2K_THIS s.pci_conf[0x04] = 0x01;
|
|
BX_NE2K_THIS s.pci_conf[0x0a] = 0x00;
|
|
BX_NE2K_THIS s.pci_conf[0x0b] = 0x02;
|
|
BX_NE2K_THIS s.pci_conf[0x0e] = 0x00;
|
|
BX_NE2K_THIS s.pci_conf[0x10] = 0x01;
|
|
BX_NE2K_THIS s.pci_conf[0x3d] = BX_PCI_INTA;
|
|
BX_NE2K_THIS s.base_address = 0x0;
|
|
}
|
|
#endif
|
|
|
|
if (BX_NE2K_THIS s.tx_timer_index == BX_NULL_TIMER_HANDLE) {
|
|
BX_NE2K_THIS s.tx_timer_index =
|
|
bx_pc_system.register_timer(this, tx_timer_handler, 0,
|
|
0,0, "ne2k"); // one-shot, inactive
|
|
}
|
|
// Register the IRQ and i/o port addresses
|
|
if (!BX_NE2K_THIS s.pci_enabled) {
|
|
BX_NE2K_THIS s.base_address = SIM->get_param_num("ioaddr", base)->get();
|
|
BX_NE2K_THIS s.base_irq = SIM->get_param_num("irq", base)->get();
|
|
|
|
DEV_register_irq(BX_NE2K_THIS s.base_irq, "NE2000 ethernet NIC");
|
|
|
|
DEV_register_ioread_handler_range(BX_NE2K_THIS_PTR, read_handler,
|
|
BX_NE2K_THIS s.base_address,
|
|
BX_NE2K_THIS s.base_address + 0x0F,
|
|
devname, 3);
|
|
DEV_register_iowrite_handler_range(BX_NE2K_THIS_PTR, write_handler,
|
|
BX_NE2K_THIS s.base_address,
|
|
BX_NE2K_THIS s.base_address + 0x0F,
|
|
devname, 3);
|
|
DEV_register_ioread_handler(BX_NE2K_THIS_PTR, read_handler,
|
|
BX_NE2K_THIS s.base_address + 0x10,
|
|
devname, 3);
|
|
DEV_register_iowrite_handler(BX_NE2K_THIS_PTR, write_handler,
|
|
BX_NE2K_THIS s.base_address + 0x10,
|
|
devname, 3);
|
|
DEV_register_ioread_handler(BX_NE2K_THIS_PTR, read_handler,
|
|
BX_NE2K_THIS s.base_address + 0x1F,
|
|
devname, 1);
|
|
DEV_register_iowrite_handler(BX_NE2K_THIS_PTR, write_handler,
|
|
BX_NE2K_THIS s.base_address + 0x1F,
|
|
devname, 1);
|
|
|
|
BX_INFO(("port 0x%x/32 irq %d mac %02x:%02x:%02x:%02x:%02x:%02x",
|
|
BX_NE2K_THIS s.base_address,
|
|
BX_NE2K_THIS s.base_irq,
|
|
macaddr[0], macaddr[1],
|
|
macaddr[2], macaddr[3],
|
|
macaddr[4], macaddr[5]));
|
|
} else {
|
|
BX_INFO(("%s initialized mac %02x:%02x:%02x:%02x:%02x:%02x",
|
|
devname,
|
|
macaddr[0], macaddr[1],
|
|
macaddr[2], macaddr[3],
|
|
macaddr[4], macaddr[5]));
|
|
}
|
|
|
|
// Initialise the mac address area by doubling the physical address
|
|
BX_NE2K_THIS s.macaddr[0] = macaddr[0];
|
|
BX_NE2K_THIS s.macaddr[1] = macaddr[0];
|
|
BX_NE2K_THIS s.macaddr[2] = macaddr[1];
|
|
BX_NE2K_THIS s.macaddr[3] = macaddr[1];
|
|
BX_NE2K_THIS s.macaddr[4] = macaddr[2];
|
|
BX_NE2K_THIS s.macaddr[5] = macaddr[2];
|
|
BX_NE2K_THIS s.macaddr[6] = macaddr[3];
|
|
BX_NE2K_THIS s.macaddr[7] = macaddr[3];
|
|
BX_NE2K_THIS s.macaddr[8] = macaddr[4];
|
|
BX_NE2K_THIS s.macaddr[9] = macaddr[4];
|
|
BX_NE2K_THIS s.macaddr[10] = macaddr[5];
|
|
BX_NE2K_THIS s.macaddr[11] = macaddr[5];
|
|
|
|
// ne2k signature
|
|
for (int i = 12; i < 32; i++)
|
|
BX_NE2K_THIS s.macaddr[i] = 0x57;
|
|
|
|
// Attach to the simulated ethernet dev
|
|
const char *ethmod = SIM->get_param_enum("ethmod", base)->get_selected();
|
|
BX_NE2K_THIS ethdev = eth_locator_c::create(ethmod,
|
|
SIM->get_param_string("ethdev", base)->getptr(),
|
|
(const char *) SIM->get_param_string("macaddr", base)->getptr(),
|
|
rx_handler,
|
|
this,
|
|
SIM->get_param_string("script", base)->getptr());
|
|
|
|
if (BX_NE2K_THIS ethdev == NULL) {
|
|
BX_PANIC(("could not find eth module %s", ethmod));
|
|
// if they continue, use null.
|
|
BX_INFO(("could not find eth module %s - using null instead", ethmod));
|
|
|
|
BX_NE2K_THIS ethdev = eth_locator_c::create("null", NULL,
|
|
(const char *) SIM->get_param_string("macaddr", base)->getptr(),
|
|
rx_handler,
|
|
this, "");
|
|
if (BX_NE2K_THIS ethdev == NULL)
|
|
BX_PANIC(("could not locate null module"));
|
|
}
|
|
}
|
|
|
|
void bx_ne2k_c::set_irq_level(bx_bool level)
|
|
{
|
|
if (BX_NE2K_THIS s.pci_enabled) {
|
|
#if BX_SUPPORT_PCI
|
|
DEV_pci_set_irq(BX_NE2K_THIS s.devfunc, BX_NE2K_THIS s.pci_conf[0x3d], level);
|
|
#endif
|
|
} else {
|
|
if (level) {
|
|
DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
|
|
} else {
|
|
DEV_pic_lower_irq(BX_NE2K_THIS s.base_irq);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if BX_SUPPORT_PCI
|
|
|
|
// pci configuration space read callback handler
|
|
Bit32u bx_ne2k_c::pci_read_handler(Bit8u address, unsigned io_len)
|
|
{
|
|
Bit32u value = 0;
|
|
|
|
if (io_len <= 4) {
|
|
for (unsigned i=0; i<io_len; i++) {
|
|
value |= (BX_NE2K_THIS s.pci_conf[address+i] << (i*8));
|
|
}
|
|
BX_DEBUG(("NE2000 PCI NIC read register 0x%02x value 0x%08x", address, value));
|
|
return value;
|
|
} else {
|
|
return(0xffffffff);
|
|
}
|
|
}
|
|
|
|
// pci configuration space write callback handler
|
|
void bx_ne2k_c::pci_write_handler(Bit8u address, Bit32u value, unsigned io_len)
|
|
{
|
|
Bit8u value8, oldval;
|
|
bx_bool baseaddr_change = 0;
|
|
|
|
if ((address > 0x13) && (address < 0x34))
|
|
return;
|
|
if (io_len <= 4) {
|
|
for (unsigned i=0; i<io_len; i++) {
|
|
oldval = BX_NE2K_THIS s.pci_conf[address+i];
|
|
value8 = (value >> (i*8)) & 0xFF;
|
|
switch (address+i) {
|
|
case 0x05:
|
|
case 0x06:
|
|
case 0x3d:
|
|
break;
|
|
case 0x04:
|
|
BX_NE2K_THIS s.pci_conf[address+i] = value8 & 0x03;
|
|
break;
|
|
case 0x3c:
|
|
if (value8 != oldval) {
|
|
BX_INFO(("new irq line = %d", value8));
|
|
BX_NE2K_THIS s.pci_conf[address+i] = value8;
|
|
}
|
|
break;
|
|
case 0x10:
|
|
value8 = (value8 & 0xfc) | 0x01;
|
|
case 0x11:
|
|
case 0x12:
|
|
case 0x13:
|
|
baseaddr_change |= (value8 != oldval);
|
|
default:
|
|
BX_NE2K_THIS s.pci_conf[address+i] = value8;
|
|
BX_DEBUG(("NE2000 PCI NIC write register 0x%02x value 0x%02x", address+i,
|
|
value8));
|
|
}
|
|
}
|
|
if (baseaddr_change) {
|
|
if (DEV_pci_set_base_io(BX_NE2K_THIS_PTR, read_handler, write_handler,
|
|
&BX_NE2K_THIS s.base_address,
|
|
&BX_NE2K_THIS s.pci_conf[0x10],
|
|
32, &ne2k_iomask[0], "NE2000 PCI NIC")) {
|
|
BX_INFO(("new base address: 0x%04x", BX_NE2K_THIS s.base_address));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* BX_SUPPORT_PCI */
|
|
|
|
#if BX_DEBUGGER
|
|
|
|
/*
|
|
* this implements the info ne2k commands in the debugger.
|
|
* info ne2k - shows all registers
|
|
* info ne2k page N - shows all registers in a page
|
|
* info ne2k page N reg M - shows just one register
|
|
*/
|
|
|
|
#define SHOW_FIELD(reg,field) do { \
|
|
if (n>0 && !(n%5)) dbg_printf ("\n "); \
|
|
dbg_printf ("%s=%d ", #field, BX_NE2K_THIS s.reg.field); \
|
|
n++; \
|
|
} while (0);
|
|
#define BX_HIGH_BYTE(x) ((0xff00 & (x)) >> 8)
|
|
#define BX_LOW_BYTE(x) (0x00ff & (x))
|
|
#define BX_DUPLICATE(n) if (brief && num!=n) break;
|
|
|
|
void bx_ne2k_c::print_info (FILE *fp, int page, int reg, int brief)
|
|
{
|
|
int i;
|
|
int n = 0;
|
|
if (page < 0) {
|
|
for (page=0; page<=2; page++)
|
|
theNE2kDevice->print_info (fp, page, reg, 1);
|
|
// tell them how to use this command
|
|
dbg_printf ("\nHow to use the info ne2k command:\n");
|
|
dbg_printf ("info ne2k - show all registers\n");
|
|
dbg_printf ("info ne2k page N - show registers in page N\n");
|
|
dbg_printf ("info ne2k page N reg M - show just one register\n");
|
|
return;
|
|
}
|
|
if (page > 2) {
|
|
dbg_printf ("NE2K has only pages 0, 1, and 2. Page %d is out of range.\n", page);
|
|
return;
|
|
}
|
|
if (reg < 0) {
|
|
dbg_printf ("NE2K registers, page %d\n", page);
|
|
dbg_printf ("----------------------\n");
|
|
for (reg=0; reg<=15; reg++)
|
|
theNE2kDevice->print_info (fp, page, reg, 1);
|
|
dbg_printf ("----------------------\n");
|
|
return;
|
|
}
|
|
if (reg > 15) {
|
|
dbg_printf ("NE2K has only registers 0-15 (0x0-0xf). Register %d is out of range.\n", reg);
|
|
return;
|
|
}
|
|
if (!brief) {
|
|
dbg_printf ("NE2K Info - page %d, register 0x%02x\n", page, reg);
|
|
dbg_printf ("----------------------------------\n");
|
|
}
|
|
int num = page*0x100 + reg;
|
|
switch (num) {
|
|
case 0x0000:
|
|
case 0x0100:
|
|
case 0x0200:
|
|
dbg_printf ("CR (Command register):\n ");
|
|
SHOW_FIELD (CR, stop);
|
|
SHOW_FIELD (CR, start);
|
|
SHOW_FIELD (CR, tx_packet);
|
|
SHOW_FIELD (CR, rdma_cmd);
|
|
SHOW_FIELD (CR, pgsel);
|
|
dbg_printf ("\n");
|
|
break;
|
|
case 0x0003:
|
|
dbg_printf ("BNRY = Boundary Pointer = 0x%02x\n", BX_NE2K_THIS s.bound_ptr);
|
|
break;
|
|
case 0x0004:
|
|
dbg_printf ("TSR (Transmit Status Register), read-only:\n ");
|
|
SHOW_FIELD (TSR, tx_ok);
|
|
SHOW_FIELD (TSR, reserved);
|
|
SHOW_FIELD (TSR, collided);
|
|
SHOW_FIELD (TSR, aborted);
|
|
SHOW_FIELD (TSR, no_carrier);
|
|
SHOW_FIELD (TSR, fifo_ur);
|
|
SHOW_FIELD (TSR, cd_hbeat);
|
|
SHOW_FIELD (TSR, ow_coll);
|
|
dbg_printf ("\n");
|
|
// fall through into TPSR, no break line.
|
|
case 0x0204:
|
|
dbg_printf ("TPSR = Transmit Page Start = 0x%02x\n", BX_NE2K_THIS s.tx_page_start);
|
|
break;
|
|
case 0x0005:
|
|
case 0x0006: BX_DUPLICATE(0x0005);
|
|
dbg_printf ("NCR = Number of Collisions Register (read-only) = 0x%02x\n", BX_NE2K_THIS s.num_coll);
|
|
dbg_printf ("TBCR1,TBCR0 = Transmit Byte Count = %02x %02x\n",
|
|
BX_HIGH_BYTE (BX_NE2K_THIS s.tx_bytes),
|
|
BX_LOW_BYTE (BX_NE2K_THIS s.tx_bytes));
|
|
dbg_printf ("FIFO = %02x\n", BX_NE2K_THIS s.fifo);
|
|
break;
|
|
case 0x0007:
|
|
dbg_printf ("ISR (Interrupt Status Register):\n ");
|
|
SHOW_FIELD (ISR, pkt_rx);
|
|
SHOW_FIELD (ISR, pkt_tx);
|
|
SHOW_FIELD (ISR, rx_err);
|
|
SHOW_FIELD (ISR, tx_err);
|
|
SHOW_FIELD (ISR, overwrite);
|
|
SHOW_FIELD (ISR, cnt_oflow);
|
|
SHOW_FIELD (ISR, rdma_done);
|
|
SHOW_FIELD (ISR, reset);
|
|
dbg_printf ("\n");
|
|
break;
|
|
case 0x0008:
|
|
case 0x0009: BX_DUPLICATE(0x0008);
|
|
dbg_printf ("CRDA1,0 = Current remote DMA address = %02x %02x\n",
|
|
BX_HIGH_BYTE (BX_NE2K_THIS s.remote_dma),
|
|
BX_LOW_BYTE (BX_NE2K_THIS s.remote_dma));
|
|
dbg_printf ("RSAR1,0 = Remote start address = %02x %02x\n",
|
|
BX_HIGH_BYTE(s.remote_start),
|
|
BX_LOW_BYTE(s.remote_start));
|
|
break;
|
|
case 0x000a:
|
|
case 0x000b: BX_DUPLICATE(0x000a);
|
|
dbg_printf ("RCBR1,0 = Remote byte count = %02x\n", BX_NE2K_THIS s.remote_bytes);
|
|
break;
|
|
case 0x000c:
|
|
dbg_printf ("RSR (Receive Status Register), read-only:\n ");
|
|
SHOW_FIELD (RSR, rx_ok);
|
|
SHOW_FIELD (RSR, bad_crc);
|
|
SHOW_FIELD (RSR, bad_falign);
|
|
SHOW_FIELD (RSR, fifo_or);
|
|
SHOW_FIELD (RSR, rx_missed);
|
|
SHOW_FIELD (RSR, rx_mbit);
|
|
SHOW_FIELD (RSR, rx_disabled);
|
|
SHOW_FIELD (RSR, deferred);
|
|
dbg_printf ("\n");
|
|
// fall through into RCR
|
|
case 0x020c:
|
|
dbg_printf ("RCR (Receive Configuration Register):\n ");
|
|
SHOW_FIELD (RCR, errors_ok);
|
|
SHOW_FIELD (RCR, runts_ok);
|
|
SHOW_FIELD (RCR, broadcast);
|
|
SHOW_FIELD (RCR, multicast);
|
|
SHOW_FIELD (RCR, promisc);
|
|
SHOW_FIELD (RCR, monitor);
|
|
SHOW_FIELD (RCR, reserved);
|
|
dbg_printf ("\n");
|
|
break;
|
|
case 0x000d:
|
|
dbg_printf ("CNTR0 = Tally Counter 0 (Frame alignment errors) = %02x\n",
|
|
BX_NE2K_THIS s.tallycnt_0);
|
|
// fall through into TCR
|
|
case 0x020d:
|
|
dbg_printf ("TCR (Transmit Configuration Register):\n ");
|
|
SHOW_FIELD (TCR, crc_disable);
|
|
SHOW_FIELD (TCR, loop_cntl);
|
|
SHOW_FIELD (TCR, ext_stoptx);
|
|
SHOW_FIELD (TCR, coll_prio);
|
|
SHOW_FIELD (TCR, reserved);
|
|
dbg_printf ("\n");
|
|
break;
|
|
case 0x000e:
|
|
dbg_printf ("CNTR1 = Tally Counter 1 (CRC Errors) = %02x\n",
|
|
BX_NE2K_THIS s.tallycnt_1);
|
|
// fall through into DCR
|
|
case 0x020e:
|
|
dbg_printf ("DCR (Data Configuration Register):\n ");
|
|
SHOW_FIELD (DCR, wdsize);
|
|
SHOW_FIELD (DCR, endian);
|
|
SHOW_FIELD (DCR, longaddr);
|
|
SHOW_FIELD (DCR, loop);
|
|
SHOW_FIELD (DCR, auto_rx);
|
|
SHOW_FIELD (DCR, fifo_size);
|
|
dbg_printf ("\n");
|
|
break;
|
|
case 0x000f:
|
|
dbg_printf ("CNTR2 = Tally Counter 2 (Missed Packet Errors) = %02x\n",
|
|
BX_NE2K_THIS s.tallycnt_2);
|
|
// fall through into IMR
|
|
case 0x020f:
|
|
dbg_printf ("IMR (Interrupt Mask Register)\n ");
|
|
SHOW_FIELD (IMR, rx_inte);
|
|
SHOW_FIELD (IMR, tx_inte);
|
|
SHOW_FIELD (IMR, rxerr_inte);
|
|
SHOW_FIELD (IMR, txerr_inte);
|
|
SHOW_FIELD (IMR, overw_inte);
|
|
SHOW_FIELD (IMR, cofl_inte);
|
|
SHOW_FIELD (IMR, rdma_inte);
|
|
SHOW_FIELD (IMR, reserved);
|
|
dbg_printf ("\n");
|
|
break;
|
|
case 0x0101:
|
|
case 0x0102: BX_DUPLICATE(0x0101);
|
|
case 0x0103: BX_DUPLICATE(0x0101);
|
|
case 0x0104: BX_DUPLICATE(0x0101);
|
|
case 0x0105: BX_DUPLICATE(0x0101);
|
|
case 0x0106: BX_DUPLICATE(0x0101);
|
|
dbg_printf ("MAC address registers are located at page 1, registers 1-6.\n");
|
|
dbg_printf ("The MAC address is ");
|
|
for (i=0; i<=5; i++)
|
|
dbg_printf ("%02x%c", BX_NE2K_THIS s.physaddr[i], i<5?':' : '\n');
|
|
break;
|
|
case 0x0107:
|
|
dbg_printf ("Current page is 0x%02x\n", BX_NE2K_THIS s.curr_page);
|
|
break;
|
|
case 0x0108:
|
|
case 0x0109: BX_DUPLICATE(0x0108);
|
|
case 0x010A: BX_DUPLICATE(0x0108);
|
|
case 0x010B: BX_DUPLICATE(0x0108);
|
|
case 0x010C: BX_DUPLICATE(0x0108);
|
|
case 0x010D: BX_DUPLICATE(0x0108);
|
|
case 0x010E: BX_DUPLICATE(0x0108);
|
|
case 0x010F: BX_DUPLICATE(0x0108);
|
|
dbg_printf ("MAR0-7 (Multicast address registers 0-7) are set to:\n");
|
|
for (i=0; i<8; i++) dbg_printf ("%02x ", BX_NE2K_THIS s.mchash[i]);
|
|
dbg_printf ("\nMAR0 is listed first.\n");
|
|
break;
|
|
case 0x0001:
|
|
case 0x0002: BX_DUPLICATE(0x0001);
|
|
case 0x0201: BX_DUPLICATE(0x0001);
|
|
case 0x0202: BX_DUPLICATE(0x0001);
|
|
dbg_printf ("PSTART = Page start register = %02x\n", BX_NE2K_THIS s.page_start);
|
|
dbg_printf ("PSTOP = Page stop register = %02x\n", BX_NE2K_THIS s.page_stop);
|
|
dbg_printf ("Local DMA address = %02x %02x\n",
|
|
BX_HIGH_BYTE(BX_NE2K_THIS s.local_dma),
|
|
BX_LOW_BYTE(BX_NE2K_THIS s.local_dma));
|
|
break;
|
|
case 0x0203:
|
|
dbg_printf ("Remote Next Packet Pointer = %02x\n", BX_NE2K_THIS s.rempkt_ptr);
|
|
break;
|
|
case 0x0205:
|
|
dbg_printf ("Local Next Packet Pointer = %02x\n", BX_NE2K_THIS s.localpkt_ptr);
|
|
break;
|
|
case 0x0206:
|
|
case 0x0207: BX_DUPLICATE(0x0206);
|
|
dbg_printf ("Address Counter= %02x %02x\n",
|
|
BX_HIGH_BYTE(BX_NE2K_THIS s.address_cnt),
|
|
BX_LOW_BYTE(BX_NE2K_THIS s.address_cnt));
|
|
break;
|
|
case 0x0208:
|
|
case 0x0209: BX_DUPLICATE(0x0208);
|
|
case 0x020A: BX_DUPLICATE(0x0208);
|
|
case 0x020B: BX_DUPLICATE(0x0208);
|
|
if (!brief) dbg_printf ("Reserved\n");
|
|
case 0xffff:
|
|
dbg_printf ("IMR (Interrupt Mask Register):\n ");
|
|
dbg_printf ("\n");
|
|
break;
|
|
default:
|
|
dbg_printf ("NE2K info: sorry, page %d register %d cannot be displayed.\n", page, reg);
|
|
}
|
|
if (!brief)
|
|
dbg_printf ("\n");
|
|
}
|
|
|
|
#else
|
|
|
|
void bx_ne2k_c::print_info (FILE *fp, int page, int reg, int brief)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif /* if BX_SUPPORT_NE2K */
|