2001-10-03 17:10:38 +04:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// $Id: ne2k.cc,v 1.22 2001-10-03 13:10:38 bdenney Exp $
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
2001-04-10 06:20:02 +04:00
|
|
|
// Copyright (C) 2001 MandrakeSoft S.A.
|
2001-04-10 05:04:59 +04:00
|
|
|
//
|
|
|
|
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
|
|
// Peter Grehan (grehan@iprg.nokia.com) coded all of this
|
|
|
|
// NE2000/ether stuff.
|
|
|
|
|
|
|
|
|
|
|
|
#include "bochs.h"
|
merge in BRANCH-io-cleanup.
To see the commit logs for this use either cvsweb or
cvs update -r BRANCH-io-cleanup and then 'cvs log' the various files.
In general this provides a generic interface for logging.
logfunctions:: is a class that is inherited by some classes, and also
. allocated as a standalone global called 'genlog'. All logging uses
. one of the ::info(), ::error(), ::ldebug(), ::panic() methods of this
. class through 'BX_INFO(), BX_ERROR(), BX_DEBUG(), BX_PANIC()' macros
. respectively.
.
. An example usage:
. BX_INFO(("Hello, World!\n"));
iofunctions:: is a class that is allocated once by default, and assigned
as the iofunction of each logfunctions instance. It is this class that
maintains the file descriptor and other output related code, at this
point using vfprintf(). At some future point, someone may choose to
write a gui 'console' for bochs to which messages would be redirected
simply by assigning a different iofunction class to the various logfunctions
objects.
More cleanup is coming, but this works for now. If you want to see alot
of debugging output, in main.cc, change onoff[LOGLEV_DEBUG]=0 to =1.
Comments, bugs, flames, to me: todd@fries.net
2001-05-15 18:49:57 +04:00
|
|
|
#define LOG_THIS bx_ne2k.
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
bx_ne2k_c bx_ne2k;
|
merge in BRANCH-io-cleanup.
To see the commit logs for this use either cvsweb or
cvs update -r BRANCH-io-cleanup and then 'cvs log' the various files.
In general this provides a generic interface for logging.
logfunctions:: is a class that is inherited by some classes, and also
. allocated as a standalone global called 'genlog'. All logging uses
. one of the ::info(), ::error(), ::ldebug(), ::panic() methods of this
. class through 'BX_INFO(), BX_ERROR(), BX_DEBUG(), BX_PANIC()' macros
. respectively.
.
. An example usage:
. BX_INFO(("Hello, World!\n"));
iofunctions:: is a class that is allocated once by default, and assigned
as the iofunction of each logfunctions instance. It is this class that
maintains the file descriptor and other output related code, at this
point using vfprintf(). At some future point, someone may choose to
write a gui 'console' for bochs to which messages would be redirected
simply by assigning a different iofunction class to the various logfunctions
objects.
More cleanup is coming, but this works for now. If you want to see alot
of debugging output, in main.cc, change onoff[LOGLEV_DEBUG]=0 to =1.
Comments, bugs, flames, to me: todd@fries.net
2001-05-15 18:49:57 +04:00
|
|
|
|
|
|
|
#if BX_USE_NE2K_SMF
|
2001-05-23 04:46:47 +04:00
|
|
|
#define this (&bx_ne2k)
|
2001-04-10 05:04:59 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
bx_ne2k_c::bx_ne2k_c(void)
|
|
|
|
{
|
2001-06-28 00:11:10 +04:00
|
|
|
put("NE2K");
|
|
|
|
settype(NE2KLOG);
|
2001-10-03 17:10:38 +04:00
|
|
|
BX_DEBUG(("Init $Id: ne2k.cc,v 1.22 2001-10-03 13:10:38 bdenney Exp $"));
|
2001-06-28 00:11:10 +04:00
|
|
|
// nothing for now
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bx_ne2k_c::~bx_ne2k_c(void)
|
|
|
|
{
|
|
|
|
// nothing for now
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// reset_device - restore state to power-up, cancelling all i/o
|
|
|
|
//
|
|
|
|
void
|
|
|
|
bx_ne2k_c::reset_device(void)
|
|
|
|
{
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG (("reset_device"));
|
2001-04-10 05:04:59 +04:00
|
|
|
// Zero out registers and memory
|
|
|
|
memset( & BX_NE2K_THIS s.CR, 0, sizeof(BX_NE2K_THIS s.CR) );
|
|
|
|
memset( & BX_NE2K_THIS s.ISR, 0, sizeof(BX_NE2K_THIS s.ISR));
|
|
|
|
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 = 1;
|
|
|
|
BX_NE2K_THIS s.ISR.reset = 1;
|
|
|
|
BX_NE2K_THIS s.DCR.longaddr = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// read_cr/write_cr - utility routines for handling reads/writes to
|
|
|
|
// the Command Register
|
|
|
|
//
|
|
|
|
Bit32u
|
|
|
|
bx_ne2k_c::read_cr(void)
|
|
|
|
{
|
2001-05-24 22:31:36 +04:00
|
|
|
Bit32u val =
|
|
|
|
(((BX_NE2K_THIS s.CR.pgsel & 0x03) << 6) |
|
2001-04-10 05:04:59 +04:00
|
|
|
((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));
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("read CR returns 0x%08x", val));
|
2001-05-24 22:31:36 +04:00
|
|
|
return val;
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_ne2k_c::write_cr(Bit32u value)
|
|
|
|
{
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("wrote 0x%08x to CR"));
|
2001-04-10 05:04:59 +04:00
|
|
|
// Validate remote-DMA
|
|
|
|
if ((value & 0x38) == 0x00)
|
2001-05-23 04:46:47 +04:00
|
|
|
return;
|
2001-05-30 22:56:02 +04:00
|
|
|
// BX_PANIC(("CR write - invalid rDMA value 0"));
|
2001-05-23 04:46:47 +04:00
|
|
|
|
2001-04-10 05:04:59 +04:00
|
|
|
// 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 start-tx
|
|
|
|
if (value & 0x04) {
|
|
|
|
if (BX_NE2K_THIS s.CR.stop || !BX_NE2K_THIS s.CR.start)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("CR write - tx start, dev in reset"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
if (BX_NE2K_THIS s.tx_bytes == 0)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("CR write - tx start, tx bytes == 0"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
#ifdef notdef
|
|
|
|
// XXX debug stuff
|
2001-05-30 22:56:02 +04:00
|
|
|
printf("packet tx (%d bytes):\t", BX_NE2K_THIS s.tx_bytes);
|
2001-04-10 05:04:59 +04:00
|
|
|
for (int i = 0; i < BX_NE2K_THIS s.tx_bytes; i++) {
|
|
|
|
printf("%02x ", BX_NE2K_THIS s.mem[BX_NE2K_THIS s.tx_page_start*256 -
|
|
|
|
BX_NE2K_MEMSTART + i]);
|
|
|
|
if (i && (((i+1) % 16) == 0))
|
2001-05-30 22:56:02 +04:00
|
|
|
printf("\t");
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
2001-05-30 22:56:02 +04:00
|
|
|
printf("");
|
2001-04-10 05:04:59 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// Send the packet to the system driver
|
2001-05-23 04:46:47 +04:00
|
|
|
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);
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// some more debug
|
|
|
|
if (BX_NE2K_THIS s.tx_timer_active)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("CR write, tx timer still active"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
BX_NE2K_THIS devices->pic->trigger_irq(BX_NE2K_THIS s.base_irq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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_ne2k_c::chipmem_read(Bit32u address, unsigned int io_len)
|
|
|
|
{
|
|
|
|
Bit32u retval = 0;
|
|
|
|
|
|
|
|
if ((io_len == 2) && (address & 0x1))
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("unaligned chipmem word read"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// ROM'd MAC address
|
|
|
|
if ((address >=0) && (address <= 31)) {
|
|
|
|
retval = BX_NE2K_THIS s.macaddr[address];
|
|
|
|
if (io_len == 2) {
|
|
|
|
retval |= (BX_NE2K_THIS s.macaddr[address + 1] << 8);
|
|
|
|
}
|
|
|
|
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) {
|
|
|
|
retval |= (BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 1] << 8);
|
|
|
|
}
|
|
|
|
return (retval);
|
|
|
|
}
|
|
|
|
|
2001-10-02 22:38:03 +04:00
|
|
|
BX_DEBUG(("out-of-bounds chipmem read, %04X", address));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
return (0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_ne2k_c::chipmem_write(Bit32u address, Bit32u value, unsigned io_len)
|
|
|
|
{
|
|
|
|
if ((io_len == 2) && (address & 0x1))
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("unaligned chipmem word write"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
if ((address >= BX_NE2K_MEMSTART) && (address <= BX_NE2K_MEMEND)) {
|
|
|
|
BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART] = value & 0xff;
|
|
|
|
if (io_len == 2)
|
|
|
|
BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 1] = value >> 8;
|
|
|
|
} else
|
2001-10-02 22:38:03 +04:00
|
|
|
BX_DEBUG(("out-of-bounds chipmem write, %04X", address));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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_ne2k_c::asic_read(Bit32u offset, unsigned int io_len)
|
|
|
|
{
|
|
|
|
Bit32u retval = 0;
|
|
|
|
|
|
|
|
switch (offset) {
|
|
|
|
case 0x0: // Data register
|
|
|
|
//
|
2001-06-26 11:49:25 +04:00
|
|
|
// 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_PANIC(("ne2K: dma read underrun"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
retval = chipmem_read(BX_NE2K_THIS s.remote_dma, io_len);
|
2001-06-26 11:49:25 +04:00
|
|
|
//
|
|
|
|
// 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).
|
|
|
|
//
|
|
|
|
BX_NE2K_THIS s.remote_dma += (BX_NE2K_THIS s.DCR.wdsize + 1);
|
|
|
|
// keep s.remote_bytes from underflowing
|
|
|
|
if (BX_NE2K_THIS s.remote_bytes > 1)
|
|
|
|
BX_NE2K_THIS s.remote_bytes -= (BX_NE2K_THIS s.DCR.wdsize + 1);
|
|
|
|
else
|
|
|
|
BX_NE2K_THIS s.remote_bytes = 0;
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xf: // Reset register
|
|
|
|
reset_device();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("asic read invalid address %04x", (unsigned) offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (retval);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_ne2k_c::asic_write(Bit32u offset, Bit32u value, unsigned io_len)
|
|
|
|
{
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("asic write addr=0x%08x, value=0x%08x", (unsigned) value, (unsigned) offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
switch (offset) {
|
|
|
|
case 0x0: // Data register - see asic_read for a description
|
|
|
|
|
|
|
|
if (io_len != (1 + BX_NE2K_THIS s.DCR.wdsize))
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("dma write, wrong size %d", io_len));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
if (BX_NE2K_THIS s.remote_bytes == 0)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("ne2K: dma write, byte count 0"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
chipmem_write(BX_NE2K_THIS s.remote_dma, value, io_len);
|
|
|
|
BX_NE2K_THIS s.remote_dma += io_len;
|
|
|
|
BX_NE2K_THIS s.remote_bytes -= io_len;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xf: // Reset register
|
|
|
|
reset_device();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("asic write invalid address %04x", (unsigned) offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
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)
|
|
|
|
{
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("page 0 read from port %04x, len=%u", (unsigned) offset,
|
2001-05-24 22:31:36 +04:00
|
|
|
(unsigned) io_len));
|
2001-10-02 22:38:03 +04:00
|
|
|
if (io_len > 1) {
|
|
|
|
BX_ERROR(("bad length! page 0 read from port %04x, len=%u", (unsigned) offset,
|
|
|
|
(unsigned) io_len)); /* encountered with win98 hardware probe */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
switch (offset) {
|
|
|
|
case 0x0: // CR
|
|
|
|
return (read_cr());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x1: // CLDA0
|
|
|
|
return (BX_NE2K_THIS s.local_dma & 0xff);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x2: // CLDA1
|
|
|
|
return (BX_NE2K_THIS s.local_dma >> 8);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x3: // BNRY
|
|
|
|
return (BX_NE2K_THIS s.bound_ptr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x4: // TSR
|
|
|
|
return ((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
|
|
|
|
return (BX_NE2K_THIS s.num_coll);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x6: // FIFO
|
|
|
|
return (BX_NE2K_THIS s.fifo);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x7: // ISR
|
|
|
|
return ((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
|
|
|
|
return (BX_NE2K_THIS s.remote_dma & 0xff);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x9: // CRDA1
|
|
|
|
return (BX_NE2K_THIS s.remote_dma >> 8);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xa: // reserved
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("reserved read - page 0, 0xa"));
|
2001-04-10 05:04:59 +04:00
|
|
|
return (0xff);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xb: // reserved
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("reserved read - page 0, 0xb"));
|
2001-04-10 05:04:59 +04:00
|
|
|
return (0xff);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xc: // RSR
|
|
|
|
return ((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
|
|
|
|
return (BX_NE2K_THIS s.tallycnt_0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xe: // CNTR1
|
|
|
|
return (BX_NE2K_THIS s.tallycnt_1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xf: // CNTR2
|
|
|
|
return (BX_NE2K_THIS s.tallycnt_2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("page 0 offset %04x out of range", (unsigned) offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_ne2k_c::page0_write(Bit32u offset, Bit32u value, unsigned io_len)
|
|
|
|
{
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("page 0 write to port %04x, len=%u", (unsigned) offset,
|
2001-05-24 22:31:36 +04:00
|
|
|
(unsigned) io_len));
|
2001-10-02 22:38:03 +04:00
|
|
|
if (io_len > 1) {
|
|
|
|
BX_ERROR(("bad length! page 0 write to port %04x, len=%u", (unsigned) offset,
|
|
|
|
(unsigned) io_len));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
switch (offset) {
|
|
|
|
case 0x0: // CR
|
|
|
|
write_cr(value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
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 &= ~((value & 0x01) == 0x01);
|
|
|
|
BX_NE2K_THIS s.ISR.pkt_tx &= ~((value & 0x02) == 0x02);
|
|
|
|
BX_NE2K_THIS s.ISR.rx_err &= ~((value & 0x04) == 0x04);
|
|
|
|
BX_NE2K_THIS s.ISR.tx_err &= ~((value & 0x08) == 0x08);
|
|
|
|
BX_NE2K_THIS s.ISR.overwrite &= ~((value & 0x10) == 0x10);
|
|
|
|
BX_NE2K_THIS s.ISR.cnt_oflow &= ~((value & 0x20) == 0x20);
|
|
|
|
BX_NE2K_THIS s.ISR.rdma_done &= ~((value & 0x40) == 0x40);
|
|
|
|
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)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("RCR write, reserved bits set"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// 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)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("RCR write, monitor bit set!"));
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xd: // TCR
|
|
|
|
// Check reserved bits
|
|
|
|
if (value & 0xe0)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("TCR write, reserved bits set"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// Test loop mode (not supported)
|
|
|
|
if (value & 0x06) {
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("TCR write, loop mode not supported"));
|
2001-04-10 05:04:59 +04:00
|
|
|
BX_NE2K_THIS s.TCR.loop_cntl = (value & 0x6) >> 1;
|
|
|
|
} else {
|
|
|
|
BX_NE2K_THIS s.TCR.loop_cntl = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inhibit-CRC not supported.
|
|
|
|
if (value & 0x01)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("TCR write, inhibit-CRC not supported"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// Auto-transmit disable very suspicious
|
|
|
|
if (value & 0x04)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("TCR write, auto transmit disable not supported"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// Allow collision-offset to be set, although not used
|
|
|
|
BX_NE2K_THIS s.TCR.coll_prio = ((value & 0x08) == 0x08);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xe: // DCR
|
|
|
|
// Don't allow loopback mode to be set
|
2001-05-25 22:44:38 +04:00
|
|
|
if (!(value & 0x08)) {
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_ERROR(("DCR write, loopback mode selected"));
|
2001-05-25 22:44:38 +04:00
|
|
|
// XXX This is a HACK, lets fix this right!
|
|
|
|
value -= 8;
|
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// 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)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("DCR write - LAS set ???"));
|
2001-04-10 05:04:59 +04:00
|
|
|
if (value & 0x10)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("DCR write - AR set ???"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// 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.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)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("IMR write, reserved bit set"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("page 0 write, bad offset %0x", offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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)
|
|
|
|
{
|
2001-06-13 20:53:58 +04:00
|
|
|
BX_DEBUG(("page 1 read from port %04x, len=%u", (unsigned) offset,
|
2001-05-24 22:31:36 +04:00
|
|
|
(unsigned) io_len));
|
2001-04-10 05:04:59 +04:00
|
|
|
if (io_len > 1)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("bad length! page 1 read from port %04x, len=%u", (unsigned) offset,
|
2001-05-23 04:46:47 +04:00
|
|
|
(unsigned) io_len));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
switch (offset) {
|
|
|
|
case 0x0: // CR
|
|
|
|
return (read_cr());
|
|
|
|
break;
|
|
|
|
|
|
|
|
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
|
|
|
|
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:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("page 1 r offset %04x out of range", (unsigned) offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_ne2k_c::page1_write(Bit32u offset, Bit32u value, unsigned io_len)
|
|
|
|
{
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("page 1 w offset %04x", (unsigned) offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
switch (offset) {
|
|
|
|
case 0x0: // CR
|
|
|
|
write_cr(value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x1: // PAR0-5
|
|
|
|
case 0x2:
|
|
|
|
case 0x3:
|
|
|
|
case 0x4:
|
|
|
|
case 0x5:
|
|
|
|
case 0x6:
|
|
|
|
BX_NE2K_THIS s.physaddr[offset - 1] = value;
|
|
|
|
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:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("page 1 w offset %04x out of range", (unsigned) offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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)
|
|
|
|
{
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("page 2 read from port %04x, len=%u", (unsigned) offset, (unsigned) io_len));
|
2001-05-24 22:31:36 +04:00
|
|
|
|
2001-04-10 05:04:59 +04:00
|
|
|
if (io_len > 1)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("bad length! page 2 read from port %04x, len=%u", (unsigned) offset, (unsigned) io_len));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
switch (offset) {
|
|
|
|
case 0x0: // CR
|
|
|
|
return (read_cr());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x1: // PSTART
|
|
|
|
return (BX_NE2K_THIS s.page_start);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x2: // PSTOP
|
|
|
|
return (BX_NE2K_THIS s.page_stop);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x3: // Remote Next-packet pointer
|
|
|
|
return (BX_NE2K_THIS s.rempkt_ptr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x4: // TPSR
|
|
|
|
return (BX_NE2K_THIS s.tx_page_start);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x5: // Local Next-packet pointer
|
|
|
|
return (BX_NE2K_THIS s.localpkt_ptr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x6: // Address counter (upper)
|
|
|
|
return (BX_NE2K_THIS s.address_cnt >> 8);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x7: // Address counter (lower)
|
|
|
|
return (BX_NE2K_THIS s.address_cnt & 0xff);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x8: // Reserved
|
|
|
|
case 0x9:
|
|
|
|
case 0xa:
|
|
|
|
case 0xb:
|
2001-06-13 20:53:58 +04:00
|
|
|
BX_ERROR(("reserved read - page 2, 0x%02x", (unsigned) offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
return (0xff);
|
|
|
|
break;
|
|
|
|
|
|
|
|
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));
|
|
|
|
break;
|
|
|
|
|
|
|
|
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));
|
|
|
|
break;
|
|
|
|
|
|
|
|
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));
|
|
|
|
break;
|
|
|
|
|
|
|
|
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));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("page 2 offset %04x out of range", (unsigned) offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_ne2k_c::page2_write(Bit32u offset, Bit32u value, unsigned io_len)
|
|
|
|
{
|
2001-05-23 04:46:47 +04:00
|
|
|
// Maybe all writes here should be BX_PANIC()'d, since they
|
2001-04-10 05:04:59 +04:00
|
|
|
// affect internal operation, but let them through for now
|
|
|
|
// and print a warning.
|
|
|
|
if (offset != 0)
|
2001-06-13 20:53:58 +04:00
|
|
|
BX_ERROR(("page 2 write ?"));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
switch (offset) {
|
|
|
|
case 0x0: // CR
|
|
|
|
write_cr(value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
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:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("page 2 write to reserved offset 4"));
|
2001-04-10 05:04:59 +04:00
|
|
|
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:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("page 2 write to reserved offset %0x", offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("page 2 write, illegal offset %0x", offset));
|
2001-04-10 05:04:59 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// page3_read/page3_write - writes to this page are illegal
|
|
|
|
//
|
|
|
|
Bit32u
|
|
|
|
bx_ne2k_c::page3_read(Bit32u offset, unsigned int io_len)
|
|
|
|
{
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("page 3 read attempted"));
|
2001-04-10 05:04:59 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_ne2k_c::page3_write(Bit32u offset, Bit32u value, unsigned io_len)
|
|
|
|
{
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("page 3 write attempted"));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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)
|
|
|
|
{
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("tx_timer"));
|
2001-04-10 05:04:59 +04:00
|
|
|
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;
|
|
|
|
BX_NE2K_THIS devices->pic->trigger_irq(BX_NE2K_THIS s.base_irq);
|
|
|
|
}
|
|
|
|
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
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("read addr %x, len %d", address, io_len));
|
2001-04-10 05:04:59 +04:00
|
|
|
Bit32u retval;
|
|
|
|
int offset = address - BX_NE2K_THIS s.base_address;
|
|
|
|
|
|
|
|
if (offset >= 0x10) {
|
|
|
|
retval = asic_read(offset - 0x10, io_len);
|
|
|
|
} 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:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("ne2K: unknown value of pgsel in read - %d",
|
2001-05-23 04:46:47 +04:00
|
|
|
BX_NE2K_THIS s.CR.pgsel));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("write with length %d", io_len));
|
2001-04-10 05:04:59 +04:00
|
|
|
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 {
|
|
|
|
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:
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("ne2K: unknown value of pgsel in write - %d",
|
2001-05-23 04:46:47 +04:00
|
|
|
BX_NE2K_THIS s.CR.pgsel));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
unsigned long 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)
|
|
|
|
{
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("rx_handler with length %d", len));
|
2001-04-10 05:04:59 +04:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
unsigned pages;
|
|
|
|
unsigned avail;
|
|
|
|
unsigned idx;
|
|
|
|
int wrapped;
|
|
|
|
int nextpage;
|
|
|
|
unsigned char pkthdr[4];
|
|
|
|
unsigned char *pktbuf = (unsigned char *) buf;
|
|
|
|
unsigned char *startptr;
|
|
|
|
static unsigned char bcast_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
|
|
|
|
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_DEBUG(("rx_frame with length %d", io_len));
|
2001-05-24 22:31:36 +04:00
|
|
|
|
2001-04-10 05:04:59 +04:00
|
|
|
if ((BX_NE2K_THIS s.CR.start == 0) ||
|
|
|
|
(BX_NE2K_THIS s.page_start == 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)/256 + 1;
|
|
|
|
|
|
|
|
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) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((io_len < 60) && !BX_NE2K_THIS s.RCR.runts_ok) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do address filtering if not in promiscuous mode
|
|
|
|
if (!BX_NE2K_THIS s.RCR.promisc) {
|
|
|
|
if (!memcmp(buf, bcast_addr, 6)) {
|
|
|
|
if (!BX_NE2K_THIS s.RCR.broadcast) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (pktbuf[0] & 0x01) {
|
|
|
|
idx = mcast_index(buf);
|
|
|
|
if (!(BX_NE2K_THIS s.mchash[idx >> 3] & (1 << (idx & 0x7)))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (memcmp(buf, BX_NE2K_THIS s.physaddr, 6)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2001-06-24 04:47:17 +04:00
|
|
|
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
|
|
|
|
}
|
2001-04-10 05:04:59 +04:00
|
|
|
pkthdr[1] = nextpage; // ptr to next packet
|
|
|
|
pkthdr[2] = (io_len + 8) & 0xff; // length-low
|
|
|
|
pkthdr[3] = (io_len + 8) >> 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;
|
|
|
|
if (pktbuf[0] & 0x80) {
|
|
|
|
BX_NE2K_THIS s.RSR.rx_mbit = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
BX_NE2K_THIS s.ISR.pkt_rx = 1;
|
|
|
|
|
|
|
|
if (BX_NE2K_THIS s.IMR.rx_inte) {
|
|
|
|
BX_NE2K_THIS devices->pic->trigger_irq(BX_NE2K_THIS s.base_irq);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
bx_ne2k_c::init(bx_devices_c *d)
|
|
|
|
{
|
2001-10-03 17:10:38 +04:00
|
|
|
BX_DEBUG(("Init $Id: ne2k.cc,v 1.22 2001-10-03 13:10:38 bdenney Exp $"));
|
2001-04-10 05:04:59 +04:00
|
|
|
BX_NE2K_THIS devices = d;
|
|
|
|
|
2001-05-23 04:46:47 +04:00
|
|
|
|
2001-06-21 22:34:50 +04:00
|
|
|
if (bx_options.ne2k.Ovalid->get ()) {
|
2001-04-10 05:04:59 +04:00
|
|
|
// Bring the register state into power-up state
|
|
|
|
reset_device();
|
|
|
|
|
|
|
|
// Read in values from config file
|
2001-06-21 22:34:50 +04:00
|
|
|
BX_NE2K_THIS s.base_address = bx_options.ne2k.Oioaddr->get ();
|
|
|
|
BX_NE2K_THIS s.base_irq = bx_options.ne2k.Oirq->get ();
|
2001-06-21 23:27:05 +04:00
|
|
|
memcpy(BX_NE2K_THIS s.physaddr, bx_options.ne2k.Omacaddr->getptr (), 6);
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
BX_NE2K_THIS s.tx_timer_index =
|
|
|
|
bx_pc_system.register_timer(this, tx_timer_handler, 0,
|
|
|
|
0,0); // one-shot, inactive
|
|
|
|
// Register the IRQ and i/o port addresses
|
|
|
|
BX_NE2K_THIS devices->register_irq(BX_NE2K_THIS s.base_irq,
|
|
|
|
"ne2000 ethernet NIC");
|
|
|
|
|
|
|
|
for (unsigned addr = BX_NE2K_THIS s.base_address;
|
|
|
|
addr <= BX_NE2K_THIS s.base_address + 0x20;
|
|
|
|
addr++) {
|
|
|
|
BX_NE2K_THIS devices->register_io_read_handler(this,
|
|
|
|
read_handler,
|
|
|
|
addr,
|
|
|
|
"ne2000 NIC");
|
|
|
|
BX_NE2K_THIS devices->register_io_write_handler(this,
|
|
|
|
write_handler,
|
|
|
|
addr,
|
|
|
|
"ne2000 NIC");
|
|
|
|
}
|
2001-10-02 22:38:03 +04:00
|
|
|
BX_INFO(("port 0x%x/32 irq %d mac %02x:%02x:%02x:%02x:%02x:%02x",
|
2001-06-13 20:53:58 +04:00
|
|
|
BX_NE2K_THIS s.base_address,
|
2001-10-02 22:38:03 +04:00
|
|
|
BX_NE2K_THIS s.base_irq,
|
2001-06-13 20:53:58 +04:00
|
|
|
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]));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
// Initialise the mac address area by doubling the physical address
|
|
|
|
BX_NE2K_THIS s.macaddr[0] = BX_NE2K_THIS s.physaddr[0];
|
|
|
|
BX_NE2K_THIS s.macaddr[1] = BX_NE2K_THIS s.physaddr[0];
|
|
|
|
BX_NE2K_THIS s.macaddr[2] = BX_NE2K_THIS s.physaddr[1];
|
|
|
|
BX_NE2K_THIS s.macaddr[3] = BX_NE2K_THIS s.physaddr[1];
|
|
|
|
BX_NE2K_THIS s.macaddr[4] = BX_NE2K_THIS s.physaddr[2];
|
|
|
|
BX_NE2K_THIS s.macaddr[5] = BX_NE2K_THIS s.physaddr[2];
|
|
|
|
BX_NE2K_THIS s.macaddr[6] = BX_NE2K_THIS s.physaddr[3];
|
|
|
|
BX_NE2K_THIS s.macaddr[7] = BX_NE2K_THIS s.physaddr[3];
|
|
|
|
BX_NE2K_THIS s.macaddr[8] = BX_NE2K_THIS s.physaddr[4];
|
|
|
|
BX_NE2K_THIS s.macaddr[9] = BX_NE2K_THIS s.physaddr[4];
|
|
|
|
BX_NE2K_THIS s.macaddr[10] = BX_NE2K_THIS s.physaddr[5];
|
|
|
|
BX_NE2K_THIS s.macaddr[11] = BX_NE2K_THIS s.physaddr[5];
|
|
|
|
|
|
|
|
// ne2k signature
|
|
|
|
for (int i = 12; i < 32; i++)
|
|
|
|
BX_NE2K_THIS s.macaddr[i] = 0x57;
|
|
|
|
|
|
|
|
// Attach to the simulated ethernet dev
|
2001-06-21 22:34:50 +04:00
|
|
|
BX_NE2K_THIS ethdev = eth_locator_c::create(bx_options.ne2k.Oethmod->getptr (),
|
|
|
|
bx_options.ne2k.Oethdev->getptr (),
|
2001-06-21 23:27:05 +04:00
|
|
|
(const char *) bx_options.ne2k.Omacaddr->getptr (),
|
2001-04-10 05:04:59 +04:00
|
|
|
rx_handler,
|
|
|
|
this);
|
|
|
|
|
|
|
|
if (BX_NE2K_THIS ethdev == NULL) {
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_INFO(("could not find eth module %s - using null instead",
|
2001-06-21 22:34:50 +04:00
|
|
|
bx_options.ne2k.Oethmod->getptr ()));
|
2001-04-10 05:04:59 +04:00
|
|
|
|
|
|
|
BX_NE2K_THIS ethdev = eth_locator_c::create("null", NULL,
|
2001-06-21 23:27:05 +04:00
|
|
|
(const char *) bx_options.ne2k.Omacaddr->getptr (),
|
2001-04-10 05:04:59 +04:00
|
|
|
rx_handler,
|
|
|
|
this);
|
|
|
|
if (BX_NE2K_THIS ethdev == NULL)
|
2001-05-30 22:56:02 +04:00
|
|
|
BX_PANIC(("could not locate null module"));
|
2001-04-10 05:04:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2001-09-29 23:16:34 +04:00
|
|
|
|
|
|
|
#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)) fprintf (fp, "\n "); \
|
|
|
|
fprintf(fp, "%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 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++)
|
|
|
|
BX_NE2K_THIS print_info (fp, page, reg, 1);
|
|
|
|
// tell them how to use this command
|
|
|
|
fprintf (fp, "\nHow to use the info ne2k command:\n");
|
|
|
|
fprintf (fp, "info ne2k - show all registers\n");
|
|
|
|
fprintf (fp, "info ne2k page N - show registers in page N\n");
|
|
|
|
fprintf (fp, "info ne2k page N reg M - show just one register\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (page > 2) {
|
|
|
|
fprintf (fp, "NE2K has only pages 0, 1, and 2. Page %d is out of range.\n", page);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (reg < 0) {
|
|
|
|
fprintf (fp, "NE2K registers, page %d\n", page);
|
|
|
|
fprintf (fp, "----------------------\n");
|
|
|
|
for (reg=0; reg<=15; reg++)
|
|
|
|
BX_NE2K_THIS print_info (fp, page, reg, 1);
|
|
|
|
fprintf (fp, "----------------------\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (reg > 15) {
|
|
|
|
fprintf (fp, "NE2K has only registers 0-15 (0x0-0xf). Register %d is out of range.\n", reg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!brief) {
|
|
|
|
fprintf (fp, "NE2K Info - page %d, register 0x%02x\n", page, reg);
|
|
|
|
fprintf (fp, "----------------------------------\n");
|
|
|
|
}
|
|
|
|
int num = page*0x100 + reg;
|
|
|
|
switch (num) {
|
|
|
|
case 0x0000:
|
|
|
|
case 0x0100:
|
|
|
|
case 0x0200:
|
|
|
|
fprintf (fp, "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);
|
|
|
|
fprintf (fp, "\n");
|
|
|
|
break;
|
|
|
|
case 0x0003:
|
|
|
|
fprintf (fp, "BNRY = Boundary Pointer = 0x%02x\n", BX_NE2K_THIS s.bound_ptr);
|
|
|
|
break;
|
|
|
|
case 0x0004:
|
|
|
|
fprintf (fp, "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);
|
|
|
|
fprintf (fp, "\n");
|
|
|
|
// fall through into TPSR, no break line.
|
|
|
|
case 0x0204:
|
|
|
|
fprintf (fp, "TPSR = Transmit Page Start = 0x%02x\n", BX_NE2K_THIS s.tx_page_start);
|
|
|
|
break;
|
|
|
|
case 0x0005:
|
|
|
|
case 0x0006: DUPLICATE(0x0005);
|
|
|
|
fprintf (fp, "NCR = Number of Collisions Register (read-only) = 0x%02x\n", BX_NE2K_THIS s.num_coll);
|
|
|
|
fprintf (fp, "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));
|
|
|
|
fprintf (fp, "FIFO = %02x\n", BX_NE2K_THIS s.fifo);
|
|
|
|
break;
|
|
|
|
case 0x0007:
|
|
|
|
fprintf (fp, "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);
|
|
|
|
fprintf (fp, "\n");
|
|
|
|
break;
|
|
|
|
case 0x0008:
|
|
|
|
case 0x0009: DUPLICATE(0x0008);
|
|
|
|
fprintf (fp, "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));
|
|
|
|
fprintf (fp, "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: DUPLICATE(0x000a);
|
|
|
|
fprintf (fp, "RCBR1,0 = Remote byte count = %02x\n", BX_NE2K_THIS s.remote_bytes);
|
|
|
|
break;
|
|
|
|
case 0x000c:
|
|
|
|
fprintf (fp, "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);
|
|
|
|
fprintf (fp, "\n");
|
|
|
|
// fall through into RCR
|
|
|
|
case 0x020c:
|
|
|
|
fprintf (fp, "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);
|
|
|
|
fprintf (fp, "\n");
|
|
|
|
break;
|
|
|
|
case 0x000d:
|
|
|
|
fprintf (fp, "CNTR0 = Tally Counter 0 (Frame alignment errors) = %02x\n",
|
|
|
|
BX_NE2K_THIS s.tallycnt_0);
|
|
|
|
// fall through into TCR
|
|
|
|
case 0x020d:
|
|
|
|
fprintf (fp, "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);
|
|
|
|
fprintf (fp, "\n");
|
|
|
|
break;
|
|
|
|
case 0x000e:
|
|
|
|
fprintf (fp, "CNTR1 = Tally Counter 1 (CRC Errors) = %02x\n",
|
|
|
|
BX_NE2K_THIS s.tallycnt_1);
|
|
|
|
// fall through into DCR
|
|
|
|
case 0x020e:
|
|
|
|
fprintf (fp, "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);
|
|
|
|
fprintf (fp, "\n");
|
|
|
|
break;
|
|
|
|
case 0x000f:
|
|
|
|
fprintf (fp, "CNTR2 = Tally Counter 2 (Missed Packet Errors) = %02x\n",
|
|
|
|
BX_NE2K_THIS s.tallycnt_2);
|
|
|
|
// fall through into IMR
|
|
|
|
case 0x020f:
|
|
|
|
fprintf (fp, "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);
|
|
|
|
fprintf (fp, "\n");
|
|
|
|
break;
|
|
|
|
case 0x0101:
|
|
|
|
case 0x0102: DUPLICATE(0x0101);
|
|
|
|
case 0x0103: DUPLICATE(0x0101);
|
|
|
|
case 0x0104: DUPLICATE(0x0101);
|
|
|
|
case 0x0105: DUPLICATE(0x0101);
|
|
|
|
case 0x0106: DUPLICATE(0x0101);
|
|
|
|
fprintf (fp, "MAC address registers are located at page 1, registers 1-6.\n");
|
|
|
|
fprintf (fp, "The MAC address is ");
|
|
|
|
for (i=0; i<=5; i++)
|
|
|
|
fprintf (fp, "%02x%c", BX_NE2K_THIS s.physaddr[i], i<5?':' : '\n');
|
|
|
|
break;
|
|
|
|
case 0x0107:
|
|
|
|
fprintf (fp, "Current page is 0x%02x\n", BX_NE2K_THIS s.curr_page);
|
|
|
|
break;
|
|
|
|
case 0x0108:
|
|
|
|
case 0x0109: DUPLICATE(0x0108);
|
|
|
|
case 0x010A: DUPLICATE(0x0108);
|
|
|
|
case 0x010B: DUPLICATE(0x0108);
|
|
|
|
case 0x010C: DUPLICATE(0x0108);
|
|
|
|
case 0x010D: DUPLICATE(0x0108);
|
|
|
|
case 0x010E: DUPLICATE(0x0108);
|
|
|
|
case 0x010F: DUPLICATE(0x0108);
|
|
|
|
fprintf (fp, "MAR0-7 (Multicast address registers 0-7) are set to:\n");
|
|
|
|
for (i=0; i<8; i++) fprintf (fp, "%02x ", BX_NE2K_THIS s.mchash[i]);
|
|
|
|
fprintf (fp, "\nMAR0 is listed first.\n");
|
|
|
|
break;
|
|
|
|
case 0x0001:
|
|
|
|
case 0x0002: DUPLICATE(0x0001);
|
|
|
|
case 0x0201: DUPLICATE(0x0001);
|
|
|
|
case 0x0202: DUPLICATE(0x0001);
|
|
|
|
fprintf (fp, "PSTART = Page start register = %02x\n", BX_NE2K_THIS s.page_start);
|
|
|
|
fprintf (fp, "PSTOP = Page stop register = %02x\n", BX_NE2K_THIS s.page_stop);
|
|
|
|
fprintf (fp, "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:
|
|
|
|
fprintf (fp, "Remote Next Packet Pointer = %02x\n", BX_NE2K_THIS s.rempkt_ptr);
|
|
|
|
break;
|
|
|
|
case 0x0205:
|
|
|
|
fprintf (fp, "Local Next Packet Pointer = %02x\n", BX_NE2K_THIS s.localpkt_ptr);
|
|
|
|
break;
|
|
|
|
case 0x0206:
|
|
|
|
case 0x0207: DUPLICATE(0x0206);
|
|
|
|
fprintf (fp, "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: DUPLICATE(0x0208);
|
|
|
|
case 0x020A: DUPLICATE(0x0208);
|
|
|
|
case 0x020B: DUPLICATE(0x0208);
|
|
|
|
if (!brief) fprintf (fp, "Reserved\n");
|
|
|
|
case 0xffff:
|
|
|
|
fprintf (fp, "IMR (Interrupt Mask Register):\n ");
|
|
|
|
fprintf (fp, "\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf (fp, "NE2K info: sorry, page %d register %d cannot be displayed.\n", page, reg);
|
|
|
|
}
|
|
|
|
if (!brief)
|
|
|
|
fprintf (fp, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|