Skip to content

Instantly share code, notes, and snippets.

@gammy
Created October 27, 2015 23:27
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gammy/99b17dae786466abfcf7 to your computer and use it in GitHub Desktop.
Save gammy/99b17dae786466abfcf7 to your computer and use it in GitHub Desktop.
An updated patch for NE2000 emulation in dosbox. More to follow.
diff --git a/configure.ac b/configure.ac
index dc2aa0f..a8c552e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -372,6 +372,16 @@ else
AC_MSG_WARN([Can't find libpng, screenshot support disabled])
fi
+AH_TEMPLATE(C_NE2000,[Define to 1 to enable NE2000 ethernet passthrough, requires libpcap])
+AC_CHECK_HEADER(pcap.h,have_pcap_h=yes,)
+AC_CHECK_LIB(pcap, pcap_open_live, have_pcap_lib=yes, ,-lz)
+if test x$have_pcap_lib = xyes -a x$have_pcap_h = xyes ; then
+ LIBS="$LIBS -lpcap";
+ AC_DEFINE(C_NE2000,1)
+else
+ AC_MSG_WARN([Can't find libpcap, NE2000 ethernet passthrough disabled])
+fi
+
AH_TEMPLATE(C_MODEM,[Define to 1 to enable internal modem support, requires SDL_net])
AH_TEMPLATE(C_IPX,[Define to 1 to enable IPX over Internet networking, requires SDL_net])
AC_CHECK_HEADER(SDL_net.h,have_sdl_net_h=yes,)
diff --git a/include/Makefile.am b/include/Makefile.am
index 5c89f48..36d55fd 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -37,5 +37,6 @@ shell.h \
support.h \
timer.h \
vga.h \
-video.h
+video.h \
+ne2000.h
diff --git a/include/ne2000.h b/include/ne2000.h
new file mode 100644
index 0000000..b1b18e3
--- /dev/null
+++ b/include/ne2000.h
@@ -0,0 +1,259 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: ne2k.h,v 1.11.2.3 2003/04/06 17:29:49 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// Peter Grehan (grehan@iprg.nokia.com) coded all of this
+// NE2000/ether stuff.
+
+//
+// An implementation of an ne2000 ISA ethernet adapter. This part uses
+// a National Semiconductor DS-8390 ethernet MAC chip, with some h/w
+// to provide a windowed memory region for the chip and a MAC address.
+//
+
+#include "dosbox.h"
+
+#define bx_bool int
+#define bx_param_c Bit8u
+
+//#define NE2K_DEBUGMSG
+
+# define BX_NE2K_SMF
+# define BX_NE2K_THIS_PTR
+# define BX_NE2K_THIS
+
+//LOG_MSG
+#ifdef NE2K_DEBUGMSG
+#define BX_INFO LOG_MSG
+#define BX_DEBUG LOG_MSG
+#else
+#define BX_INFO
+#define BX_DEBUG
+#endif
+//LOG_MSG
+
+#define BX_NE2K_MEMSIZ (32*1024)
+#define BX_NE2K_MEMSTART (16*1024)
+#define BX_NE2K_MEMEND (BX_NE2K_MEMSTART + BX_NE2K_MEMSIZ)
+
+typedef struct {
+ //
+ // ne2k register state
+
+ //
+ // Page 0
+ //
+ // Command Register - 00h read/write
+ struct CR_t {
+ bx_bool stop; // STP - Software Reset command
+ bx_bool start; // START - start the NIC
+ bx_bool tx_packet; // TXP - initiate packet transmission
+ Bit8u rdma_cmd; // RD0,RD1,RD2 - Remote DMA command
+ Bit8u pgsel; // PS0,PS1 - Page select
+ } CR;
+ // Interrupt Status Register - 07h read/write
+ struct ISR_t {
+ bx_bool pkt_rx; // PRX - packet received with no errors
+ bx_bool pkt_tx; // PTX - packet transmitted with no errors
+ bx_bool rx_err; // RXE - packet received with 1 or more errors
+ bx_bool tx_err; // TXE - packet tx'd " " " " "
+ bx_bool overwrite; // OVW - rx buffer resources exhausted
+ bx_bool cnt_oflow; // CNT - network tally counter MSB's set
+ bx_bool rdma_done; // RDC - remote DMA complete
+ bx_bool reset; // RST - reset status
+ } ISR;
+ // Interrupt Mask Register - 0fh write
+ struct IMR_t {
+ bx_bool rx_inte; // PRXE - packet rx interrupt enable
+ bx_bool tx_inte; // PTXE - packet tx interrput enable
+ bx_bool rxerr_inte; // RXEE - rx error interrupt enable
+ bx_bool txerr_inte; // TXEE - tx error interrupt enable
+ bx_bool overw_inte; // OVWE - overwrite warn int enable
+ bx_bool cofl_inte; // CNTE - counter o'flow int enable
+ bx_bool rdma_inte; // RDCE - remote DMA complete int enable
+ bx_bool reserved; // D7 - reserved
+ } IMR;
+ // Data Configuration Register - 0eh write
+ struct DCR_t {
+ bx_bool wdsize; // WTS - 8/16-bit select
+ bx_bool endian; // BOS - byte-order select
+ bx_bool longaddr; // LAS - long-address select
+ bx_bool loop; // LS - loopback select
+ bx_bool auto_rx; // AR - auto-remove rx packets with remote DMA
+ Bit8u fifo_size; // FT0,FT1 - fifo threshold
+ } DCR;
+ // Transmit Configuration Register - 0dh write
+ struct TCR_t {
+ bx_bool crc_disable; // CRC - inhibit tx CRC
+ Bit8u loop_cntl; // LB0,LB1 - loopback control
+ bx_bool ext_stoptx; // ATD - allow tx disable by external mcast
+ bx_bool coll_prio; // OFST - backoff algorithm select
+ Bit8u reserved; // D5,D6,D7 - reserved
+ } TCR;
+ // Transmit Status Register - 04h read
+ struct TSR_t {
+ bx_bool tx_ok; // PTX - tx complete without error
+ bx_bool reserved; // D1 - reserved
+ bx_bool collided; // COL - tx collided >= 1 times
+ bx_bool aborted; // ABT - aborted due to excessive collisions
+ bx_bool no_carrier; // CRS - carrier-sense lost
+ bx_bool fifo_ur; // FU - FIFO underrun
+ bx_bool cd_hbeat; // CDH - no tx cd-heartbeat from transceiver
+ bx_bool ow_coll; // OWC - out-of-window collision
+ } TSR;
+ // Receive Configuration Register - 0ch write
+ struct RCR_t {
+ bx_bool errors_ok; // SEP - accept pkts with rx errors
+ bx_bool runts_ok; // AR - accept < 64-byte runts
+ bx_bool broadcast; // AB - accept eth broadcast address
+ bx_bool multicast; // AM - check mcast hash array
+ bx_bool promisc; // PRO - accept all packets
+ bx_bool monitor; // MON - check pkts, but don't rx
+ Bit8u reserved; // D6,D7 - reserved
+ } RCR;
+ // Receive Status Register - 0ch read
+ struct RSR_t {
+ bx_bool rx_ok; // PRX - rx complete without error
+ bx_bool bad_crc; // CRC - Bad CRC detected
+ bx_bool bad_falign; // FAE - frame alignment error
+ bx_bool fifo_or; // FO - FIFO overrun
+ bx_bool rx_missed; // MPA - missed packet error
+ bx_bool rx_mbit; // PHY - unicast or mcast/bcast address match
+ bx_bool rx_disabled; // DIS - set when in monitor mode
+ bx_bool deferred; // DFR - collision active
+ } RSR;
+
+ Bit16u local_dma; // 01,02h read ; current local DMA addr
+ Bit8u page_start; // 01h write ; page start register
+ Bit8u page_stop; // 02h write ; page stop register
+ Bit8u bound_ptr; // 03h read/write ; boundary pointer
+ Bit8u tx_page_start; // 04h write ; transmit page start register
+ Bit8u num_coll; // 05h read ; number-of-collisions register
+ Bit16u tx_bytes; // 05,06h write ; transmit byte-count register
+ Bit8u fifo; // 06h read ; FIFO
+ Bit16u remote_dma; // 08,09h read ; current remote DMA addr
+ Bit16u remote_start; // 08,09h write ; remote start address register
+ Bit16u remote_bytes; // 0a,0bh write ; remote byte-count register
+ Bit8u tallycnt_0; // 0dh read ; tally counter 0 (frame align errors)
+ Bit8u tallycnt_1; // 0eh read ; tally counter 1 (CRC errors)
+ Bit8u tallycnt_2; // 0fh read ; tally counter 2 (missed pkt errors)
+
+ //
+ // Page 1
+ //
+ // Command Register 00h (repeated)
+ //
+ Bit8u physaddr[6]; // 01-06h read/write ; MAC address
+ Bit8u curr_page; // 07h read/write ; current page register
+ Bit8u mchash[8]; // 08-0fh read/write ; multicast hash array
+
+ //
+ // Page 2 - diagnostic use only
+ //
+ // Command Register 00h (repeated)
+ //
+ // Page Start Register 01h read (repeated)
+ // Page Stop Register 02h read (repeated)
+ // Current Local DMA Address 01,02h write (repeated)
+ // Transmit Page start address 04h read (repeated)
+ // Receive Configuration Register 0ch read (repeated)
+ // Transmit Configuration Register 0dh read (repeated)
+ // Data Configuration Register 0eh read (repeated)
+ // Interrupt Mask Register 0fh read (repeated)
+ //
+ Bit8u rempkt_ptr; // 03h read/write ; remote next-packet pointer
+ Bit8u localpkt_ptr; // 05h read/write ; local next-packet pointer
+ Bit16u address_cnt; // 06,07h read/write ; address counter
+
+ //
+ // Page 3 - should never be modified.
+ //
+
+ // Novell ASIC state
+ Bit8u macaddr[32]; // ASIC ROM'd MAC address, even bytes
+ Bit8u mem[BX_NE2K_MEMSIZ]; // on-chip packet memory
+
+ // ne2k internal state
+ Bit32u base_address;
+ int base_irq;
+ int tx_timer_index;
+ int tx_timer_active;
+
+ void register_state(bx_param_c *list_p);
+
+} bx_ne2k_t;
+
+
+
+class bx_ne2k_c {
+public:
+
+ bx_ne2k_c(void);
+ ~bx_ne2k_c(void);
+
+ virtual void init(void);
+ virtual void reset(unsigned type);
+
+public:
+ bx_ne2k_t s;
+ /* TODO: Setup SDL */
+ //eth_pktmover_c *ethdev;
+
+ BX_NE2K_SMF Bit32u read_cr(void);
+ BX_NE2K_SMF void write_cr(Bit32u value);
+
+ BX_NE2K_SMF Bit32u chipmem_read(Bit32u address, unsigned io_len);
+ BX_NE2K_SMF Bit32u asic_read(Bit32u offset, unsigned io_len);
+ BX_NE2K_SMF Bit32u page0_read(Bit32u offset, unsigned io_len);
+ BX_NE2K_SMF Bit32u page1_read(Bit32u offset, unsigned io_len);
+ BX_NE2K_SMF Bit32u page2_read(Bit32u offset, unsigned io_len);
+ BX_NE2K_SMF Bit32u page3_read(Bit32u offset, unsigned io_len);
+
+ BX_NE2K_SMF void chipmem_write(Bit32u address, Bit32u value, unsigned io_len);
+ BX_NE2K_SMF void asic_write(Bit32u address, Bit32u value, unsigned io_len);
+ BX_NE2K_SMF void page0_write(Bit32u address, Bit32u value, unsigned io_len);
+ BX_NE2K_SMF void page1_write(Bit32u address, Bit32u value, unsigned io_len);
+ BX_NE2K_SMF void page2_write(Bit32u address, Bit32u value, unsigned io_len);
+ BX_NE2K_SMF void page3_write(Bit32u address, Bit32u value, unsigned io_len);
+
+public:
+ static void tx_timer_handler(void *);
+ BX_NE2K_SMF void tx_timer(void);
+
+ static void rx_handler(void *arg, const void *buf, unsigned len);
+ BX_NE2K_SMF unsigned mcast_index(const void *dst);
+ BX_NE2K_SMF void rx_frame(const void *buf, unsigned io_len);
+
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_NE2K_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+#endif
+
+
+};
+
diff --git a/ne2000_27_11_10.diff b/ne2000_27_11_10.diff
new file mode 100644
index 0000000..bfb0bfc
--- /dev/null
+++ b/ne2000_27_11_10.diff
@@ -0,0 +1,2046 @@
+Index: visualc_net/dosbox.vcproj
+===================================================================
+--- visualc_net/dosbox.vcproj (revision 3661)
++++ visualc_net/dosbox.vcproj (working copy)
+@@ -480,6 +480,9 @@
+ <File
+ RelativePath="..\src\hardware\mixer.cpp">
+ </File>
++ <File
++ RelativePath="..\src\hardware\ne2000.cpp">
++ </File>
+ <File
+ RelativePath="..\src\hardware\pic.cpp">
+ </File>
+@@ -828,6 +831,9 @@
+ <File
+ RelativePath="..\include\mouse.h">
+ </File>
++ <File
++ RelativePath="..\include\ne2000.h">
++ </File>
+ <File
+ RelativePath="..\include\paging.h">
+ </File>
+Index: src/platform/visualc/config.h
+===================================================================
+--- src/platform/visualc/config.h (revision 3661)
++++ src/platform/visualc/config.h (working copy)
+@@ -12,6 +12,9 @@
+ /* Define to 1 to enable internal modem support, requires SDL_net */
+ #define C_MODEM 1
+
++/* Define to 1 to enable NE2000 ethernet passthrough, requires libpcap */
++#define C_NE2000 1
++
+ /* Define to 1 to enable IPX networking support, requires SDL_net */
+ #define C_IPX 1
+
+Index: src/hardware/ne2000.cpp
+===================================================================
+--- src/hardware/ne2000.cpp (revision 0)
++++ src/hardware/ne2000.cpp (revision 0)
+@@ -0,0 +1,1660 @@
++#include "config.h"
++
++#ifdef C_NE2000
++
++
++#include "dosbox.h"
++#include <string.h>
++#include <stdio.h>
++#include "support.h"
++#include "inout.h"
++#include "setup.h"
++#include "callback.h"
++#include "timer.h"
++#include "pic.h"
++#include "cpu.h"
++
++/* Couldn't find a real spec for the NE2000 out there, hence this is adapted heavily from Bochs */
++
++
++/////////////////////////////////////////////////////////////////////////
++// $Id: ne2k.cc,v 1.56.2.1 2004/02/02 22:37:22 cbothamy 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++// Peter Grehan (grehan@iprg.nokia.com) coded all of this
++// NE2000/ether stuff.
++
++#include "ne2000.h"
++
++#define HAVE_REMOTE
++
++#include "pcap.h"
++// Handle to WinPCap device
++pcap_t *adhandle = 0;
++static void NE2000_TX_Event(Bitu val);
++
++#ifdef WIN32
++// DLL loading
++#define pcap_sendpacket(A,B,C) PacketSendPacket(A,B,C)
++#define pcap_close(A) PacketClose(A)
++#define pcap_freealldevs(A) PacketFreealldevs(A)
++#define pcap_open(A,B,C,D,E,F) PacketOpen(A,B,C,D,E,F)
++#define pcap_next_ex(A,B,C) PacketNextEx(A,B,C)
++#define pcap_findalldevs_ex(A,B,C,D) PacketFindALlDevsEx(A,B,C,D)
++
++int (*PacketSendPacket)(pcap_t *, const u_char *, int) = 0;
++void (*PacketClose)(pcap_t *) = 0;
++void (*PacketFreealldevs)(pcap_if_t *) = 0;
++pcap_t* (*PacketOpen)(char const *,int,int,int,struct pcap_rmtauth *,char *) = 0;
++int (*PacketNextEx)(pcap_t *, struct pcap_pkthdr **, const u_char **) = 0;
++int (*PacketFindALlDevsEx)(char *, struct pcap_rmtauth *, pcap_if_t **, char *) = 0;
++
++#endif
++
++//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->
++//#define BX_DEBUG
++//#define BX_INFO
++#define BX_NULL_TIMER_HANDLE NULL
++#define BX_PANIC
++#define BX_ERROR
++#define BX_RESET_HARDWARE 0
++#define BX_RESET_SOFTWARE 1
++
++bx_ne2k_c* theNE2kDevice = NULL;
++
++
++bx_ne2k_c::bx_ne2k_c(void)
++{
++ s.tx_timer_index = BX_NULL_TIMER_HANDLE;
++}
++
++
++bx_ne2k_c::~bx_ne2k_c(void)
++{
++ // nothing for now
++}
++
++//
++// reset - restore state to power-up, cancelling all i/o
++//
++void
++bx_ne2k_c::reset(unsigned type)
++{
++ BX_DEBUG ("reset");
++ // 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.tx_timer_active = 0;
++ 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.ISR.reset = 1;
++ BX_NE2K_THIS s.DCR.longaddr = 1;
++ PIC_DeActivateIRQ(s.base_irq);
++ //DEV_pic_lower_irq(BX_NE2K_THIS s.base_irq);
++}
++
++//
++// 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%08x", 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 */
++ //value = 0x22; /* 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) {
++ // loopback mode
++ 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);
++
++ // do a TX interrupt
++ // 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) {
++ //LOG_MSG("tx complete interrupt");
++ PIC_ActivateIRQ(s.base_irq);
++ }
++ BX_NE2K_THIS s.ISR.pkt_tx = 1;
++ }
++ } else if (value & 0x04) {
++ // start-tx and no loopback
++ if (BX_NE2K_THIS s.CR.stop || !BX_NE2K_THIS s.CR.start)
++ 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"));
++
++#ifdef notdef
++ // XXX debug stuff
++ printf("packet tx (%d bytes):\t", BX_NE2K_THIS s.tx_bytes);
++ 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))
++ printf("\t");
++ }
++ printf("");
++#endif
++
++ // Send the packet to the system driver
++ /* TODO: Transmit packet */
++ //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);
++ pcap_sendpacket(adhandle,&s.mem[s.tx_page_start*256 - BX_NE2K_MEMSTART], s.tx_bytes);
++ // some more debug
++ if (BX_NE2K_THIS s.tx_timer_active) {
++ BX_PANIC(("CR write, tx timer still active"));
++ PIC_RemoveEvents(NE2000_TX_Event);
++ }
++ //LOG_MSG("send packet command");
++ //s.tx_timer_index = (64 + 96 + 4*8 + BX_NE2K_THIS s.tx_bytes*8)/10;
++ s.tx_timer_active = 1;
++ PIC_AddEvent(NE2000_TX_Event,(float)((64 + 96 + 4*8 + BX_NE2K_THIS s.tx_bytes*8)/10000.0),0);
++ // 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).
++ //
++
++ /* TODO: Code transmit timer */
++ /*
++ 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
++ */
++ } // end transmit-start branch
++
++ // 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) {
++ PIC_ActivateIRQ(s.base_irq);
++ //DEV_pic_raise_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))
++ 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_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)
++ BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 1] = value >> 8;
++ } 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_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).
++ //
++ 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 > 1)
++ 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) {
++ PIC_ActivateIRQ(s.base_irq);
++ //DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
++ }
++ }
++ break;
++
++ case 0xf: // Reset register
++ theNE2kDevice->reset(BX_RESET_SOFTWARE);
++ //retval=0x1;
++ 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 == 2) && (BX_NE2K_THIS s.DCR.wdsize == 0)) {
++ BX_PANIC(("dma write length 2 on byte mode operation"));
++ break;
++ }
++
++ if (BX_NE2K_THIS s.remote_bytes == 0)
++ BX_PANIC(("ne2K: dma write, byte count 0"));
++
++ chipmem_write(BX_NE2K_THIS s.remote_dma, value, io_len);
++ // is this right ??? asic_read uses DCR.wordsize
++ BX_NE2K_THIS s.remote_dma += io_len;
++ 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;
++ }
++
++ BX_NE2K_THIS s.remote_bytes -= io_len;
++ 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) {
++ PIC_ActivateIRQ(s.base_irq);
++ //DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
++ }
++ }
++ break;
++
++ case 0xf: // Reset register
++ theNE2kDevice->reset(BX_RESET_SOFTWARE);
++ 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)
++{
++ BX_DEBUG("page 0 read from port %04x, len=%u", (unsigned) offset,
++ (unsigned) io_len);
++ 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;
++ }
++
++
++ switch (offset) {
++ 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
++ // reading FIFO is only valid in loopback mode
++ BX_ERROR(("reading FIFO not supported yet"));
++ 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
++ BX_INFO(("reserved read - page 0, 0xa"));
++ return (0xff);
++ break;
++
++ case 0xb: // reserved
++ BX_INFO(("reserved read - page 0, 0xb"));
++ 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:
++ BX_PANIC("page 0 offset %04x out of range", (unsigned) offset);
++ }
++
++ return(0);
++}
++
++void
++bx_ne2k_c::page0_write(Bit32u offset, Bit32u value, unsigned io_len)
++{
++ BX_DEBUG("page 0 write to port %04x, len=%u", (unsigned) offset,
++ (unsigned) io_len);
++
++ // 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);
++ page0_write(offset + 1, ((value >> 8) & 0xff), 1);
++ return;
++ }
++
++ switch (offset) {
++ case 0x1: // PSTART
++ BX_NE2K_THIS s.page_start = value;
++ break;
++
++ case 0x2: // PSTOP
++ // BX_INFO(("Writing to PSTOP: %02x", value));
++ 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)
++ PIC_DeActivateIRQ(s.base_irq);
++ //DEV_pic_lower_irq(BX_NE2K_THIS s.base_irq);
++ 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_PANIC(("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);
++ if(BX_NE2K_THIS s.ISR.pkt_tx && BX_NE2K_THIS s.IMR.tx_inte) {
++ LOG_MSG("tx irq retrigger");
++ PIC_ActivateIRQ(s.base_irq);
++ }
++ break;
++ default:
++ BX_PANIC("page 0 write, bad offset %0x", 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 port %04x, len=%u", (unsigned) offset,
++ (unsigned) io_len);
++ if (io_len > 1)
++ BX_PANIC("bad length! page 1 read from port %04x, len=%u", (unsigned) offset,
++ (unsigned) 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: %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 r offset %04x out of range", (unsigned) offset);
++ }
++
++ return (0);
++}
++
++void
++bx_ne2k_c::page1_write(Bit32u offset, Bit32u value, unsigned io_len)
++{
++ BX_DEBUG("page 1 w offset %04x", (unsigned) offset);
++ switch (offset) {
++ 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:
++ BX_PANIC("page 1 w offset %04x out of range", (unsigned) 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 port %04x, len=%u", (unsigned) offset, (unsigned) io_len);
++
++ if (io_len > 1)
++ BX_PANIC("bad length! page 2 read from port %04x, len=%u", (unsigned) offset, (unsigned) io_len);
++
++ switch (offset) {
++ 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:
++ BX_ERROR("reserved read - page 2, 0x%02x", (unsigned) offset);
++ 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:
++ BX_PANIC("page 2 offset %04x out of range", (unsigned) 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.
++ if (offset != 0)
++ BX_ERROR(("page 2 write ?"));
++
++ 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 offset 4"));
++ 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 offset %0x", offset);
++ break;
++
++ default:
++ BX_PANIC("page 2 write, illegal offset %0x", offset);
++ break;
++ }
++}
++
++//
++// page3_read/page3_write - writes to this page are illegal
++//
++Bit32u
++bx_ne2k_c::page3_read(Bit32u offset, unsigned int io_len)
++{
++ BX_PANIC(("page 3 read attempted"));
++ return (0);
++}
++
++void
++bx_ne2k_c::page3_write(Bit32u offset, Bit32u value, unsigned io_len)
++{
++ BX_PANIC(("page 3 write attempted"));
++}
++
++//
++// 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.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) {
++ //LOG_MSG("tx complete interrupt");
++ PIC_ActivateIRQ(s.base_irq);
++ //DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
++ } //else LOG_MSG("no tx complete interrupt");
++ BX_NE2K_THIS s.ISR.pkt_tx = 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 with length %d", 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
++ 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)
++{
++ // BX_DEBUG(("rx_handler with length %d", len));
++ bx_ne2k_c *class_ptr = (bx_ne2k_c *) arg;
++ if(
++ 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;
++ static unsigned char bcast_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
++
++ if(io_len != 60) {
++ BX_DEBUG("rx_frame with length %d", io_len);
++ }
++
++ //LOG_MSG("stop=%d, pagestart=%x, dcr_loop=%x, tcr_loopcntl=%x",
++ // BX_NE2K_THIS s.CR.stop, BX_NE2K_THIS s.page_start,
++ // BX_NE2K_THIS s.DCR.loop, BX_NE2K_THIS s.TCR.loop_cntl);
++ 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
++ ) {
++ BX_DEBUG("no space");
++ return;
++ }
++
++ if ((io_len < 40/*60*/) && !BX_NE2K_THIS s.RCR.runts_ok) {
++ BX_DEBUG("rejected small packet, length %d", io_len);
++ return;
++ }
++ // some computers don't care...
++ if (io_len < 60) io_len=60;
++
++ // 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) {
++ 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;
++ 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) {
++ //LOG_MSG("packet rx interrupt");
++ PIC_ActivateIRQ(s.base_irq);
++ //DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
++ } //else LOG_MSG("no packet rx interrupt");
++
++}
++
++//Bit8u macaddr[6] = { 0xAC, 0xDE, 0x48, 0x8E, 0x89, 0x19 };
++
++Bitu dosbox_read(Bitu port, Bitu len) {
++ Bitu retval = theNE2kDevice->read(port,len);
++ //LOG_MSG("ne2k rd port %x val %4x len %d page %d, CS:IP %8x:%8x",
++ // port, retval, len, theNE2kDevice->s.CR.pgsel,SegValue(cs),reg_eip);
++ return retval;
++}
++void dosbox_write(Bitu port, Bitu val, Bitu len) {
++ //LOG_MSG("ne2k wr port %x val %4x len %d page %d, CS:IP %8x:%8x",
++ // port, val, len,theNE2kDevice->s.CR.pgsel,SegValue(cs),reg_eip);
++ theNE2kDevice->write(port, val, len);
++}
++
++void bx_ne2k_c::init()
++{
++ //BX_DEBUG(("Init $Id: ne2k.cc,v 1.56.2.1 2004/02/02 22:37:22 cbothamy Exp $"));
++
++ // Read in values from config file
++ //BX_NE2K_THIS s.base_address = 0x300;
++ //BX_NE2K_THIS s.base_irq = 3;
++ /*
++ 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
++ //DEV_register_irq(BX_NE2K_THIS s.base_irq, "NE2000 ethernet NIC");
++
++ //DEV_register_ioread_handler(this, read_handler, addr, "ne2000 NIC", 3);
++ //DEV_register_iowrite_handler(this, write_handler, addr, "ne2000 NIC", 3);
++
++
++ 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,
++ 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]);
++
++ // 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 (Bitu i = 12; i < 32; i++)
++ BX_NE2K_THIS s.macaddr[i] = 0x57;
++
++ // Bring the register state into power-up state
++ reset(BX_RESET_HARDWARE);
++}
++
++static void NE2000_TX_Event(Bitu val) {
++ theNE2kDevice->tx_timer();
++}
++
++static void NE2000_Poller(void) {
++ int res;
++ struct pcap_pkthdr *header;
++ u_char *pkt_data;
++//#if 0
++ while((res = pcap_next_ex( adhandle, &header, (const u_char **)&pkt_data)) > 0) {
++ //LOG_MSG("NE2000: Received %d bytes", header->len);
++
++ // don't receive in loopback modes
++ if((theNE2kDevice->s.DCR.loop == 0) || (theNE2kDevice->s.TCR.loop_cntl != 0))
++ return;
++ theNE2kDevice->rx_frame(pkt_data, header->len);
++ }
++//#endif
++}
++#ifdef WIN32
++#include <windows.h>
++#endif
++
++class NE2K: public Module_base {
++private:
++ // Data
++ IO_ReadHandleObject ReadHandler8[0x20];
++ IO_WriteHandleObject WriteHandler8[0x20];
++ IO_ReadHandleObject ReadHandler16[0x10];
++ IO_WriteHandleObject WriteHandler16[0x10];
++
++public:
++ bool load_success;
++ NE2K(Section* configuration):Module_base(configuration) {
++ Section_prop * section=static_cast<Section_prop *>(configuration);
++
++ load_success = true;
++ // enabled?
++
++ if(!section->Get_bool("ne2000")) {
++ load_success = false;
++ return;
++ }
++
++#ifdef WIN32
++/*
++ int (*PacketSendPacket)(pcap_t *, const u_char *, int);
++ void (*PacketClose)(pcap_t *);
++ void (*PacketFreealldevs)(pcap_if_t *);
++ pcap_t* (*PacketOpen)(char const *,int,int,int,struct pcap_rmtauth *,char *);
++ int (*PacketNextEx)(pcap_t *, struct pcap_pkthdr **, const u_char **);
++ int (*PacketFindALlDevsEx)(char *, struct pcap_rmtauth *, pcap_if_t **, char *);
++*/
++ // init the library
++ HINSTANCE pcapinst;
++ pcapinst = LoadLibrary("WPCAP.DLL");
++ if(pcapinst==NULL) {
++ LOG_MSG("WinPcap has to be installed for the NE2000 to work.");
++ load_success = false;
++ return;
++ }
++ FARPROC psp;
++
++ psp = GetProcAddress(pcapinst,"pcap_sendpacket");
++ if(!PacketSendPacket) PacketSendPacket =
++ (int (__cdecl *)(pcap_t *,const u_char *,int))psp;
++
++ psp = GetProcAddress(pcapinst,"pcap_close");
++ if(!PacketClose) PacketClose =
++ (void (__cdecl *)(pcap_t *)) psp;
++
++ psp = GetProcAddress(pcapinst,"pcap_freealldevs");
++ if(!PacketFreealldevs) PacketFreealldevs =
++ (void (__cdecl *)(pcap_if_t *)) psp;
++
++ psp = GetProcAddress(pcapinst,"pcap_open");
++ if(!PacketOpen) PacketOpen =
++ (pcap_t* (__cdecl *)(char const *,int,int,int,struct pcap_rmtauth *,char *)) psp;
++
++ psp = GetProcAddress(pcapinst,"pcap_next_ex");
++ if(!PacketNextEx) PacketNextEx =
++ (int (__cdecl *)(pcap_t *, struct pcap_pkthdr **, const u_char **)) psp;
++
++ psp = GetProcAddress(pcapinst,"pcap_findalldevs_ex");
++ if(!PacketFindALlDevsEx) PacketFindALlDevsEx =
++ (int (__cdecl *)(char *, struct pcap_rmtauth *, pcap_if_t **, char *)) psp;
++
++ if(PacketFindALlDevsEx==0 || PacketNextEx==0 || PacketOpen==0 ||
++ PacketFreealldevs==0 || PacketClose==0 || PacketSendPacket==0) {
++ LOG_MSG("Wrong WinPcap version or something");
++ load_success = false;
++ return;
++ }
++
++#endif
++
++ // get irq and base
++ Bitu irq = section->Get_int("nicirq");
++ if(!(irq==3 || irq==4 || irq==5 || irq==6 ||irq==7 ||
++ irq==9 || irq==10 || irq==11 || irq==12 ||irq==14 ||irq==15)) {
++ irq=3;
++ }
++ Bitu base = section->Get_hex("nicbase");
++ if(!(base==0x260||base==0x280||base==0x300||base==0x320||base==0x340||base==0x380)) {
++ base=0x300;
++ }
++
++ // mac address
++ const char* macstring=section->Get_string("macaddr");
++ Bitu macint[6];
++ Bit8u mac[6];
++ if(sscanf(macstring,"%02x:%02x:%02x:%02x:%02x:%02x",
++ &macint[0],&macint[1],&macint[2],&macint[3],&macint[4],&macint[5]) != 6) {
++ mac[0]=0xac;mac[1]=0xde;mac[2]=0x48;
++ mac[3]=0x88;mac[4]=0xbb;mac[5]=0xaa;
++ } else {
++ mac[0]=macint[0]; mac[1]=macint[1];
++ mac[2]=macint[2]; mac[3]=macint[3];
++ mac[4]=macint[4]; mac[5]=macint[5];
++ }
++
++ // find out which pcap device to use
++ const char* realnicstring=section->Get_string("realnic");
++ pcap_if_t *alldevs;
++ pcap_if_t *currentdev = NULL;
++ char errbuf[PCAP_ERRBUF_SIZE];
++ Bitu userdev;
++#ifdef WIN32
++ if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
++#else
++ if (pcap_findalldevs(&alldevs, errbuf) == -1)
++#endif
++ {
++ LOG_MSG("Cannot enumerate network interfaces: %s\n", errbuf);
++ load_success = false;
++ return;
++ }
++ if (!strcasecmp(realnicstring,"list")) {
++ // print list and quit
++ Bitu i = 0;
++ LOG_MSG("\nNetwork Interface List \n-----------------------------------");
++ for(currentdev=alldevs; currentdev!=NULL; currentdev=currentdev->next) {
++ const char* desc = "no description";
++ if(currentdev->description) desc=currentdev->description;
++ i++;
++ LOG_MSG("%2d. %s\n (%s)\n",i,currentdev->name,desc);
++ }
++ pcap_freealldevs(alldevs);
++ load_success = false;
++ return;
++ } else if(1==sscanf(realnicstring,"%u",&userdev)) {
++ // user passed us a number
++ Bitu i = 0;
++ currentdev=alldevs;
++ while(currentdev!=NULL) {
++ i++;
++ if(i==userdev) break;
++ else currentdev=currentdev->next;
++ }
++ } else {
++ // user might have passed a piece of name
++ for(currentdev=alldevs; currentdev!=NULL; currentdev=currentdev->next) {
++ if(strstr(currentdev->name,realnicstring)) {
++ break;
++ }else if(currentdev->description!=NULL &&
++ strstr(currentdev->description,realnicstring)) {
++ break;
++ }
++ }
++ }
++
++ if(currentdev==NULL) {
++ LOG_MSG("Unable to find network interface - check realnic parameter\n");
++ load_success = false;
++ pcap_freealldevs(alldevs);
++ return;
++ }
++ // print out which interface we are going to use
++ const char* desc = "no description";
++ if(currentdev->description) desc=currentdev->description;
++ LOG_MSG("Using Network interface:\n%s\n(%s)\n",currentdev->name,desc);
++
++ // attempt to open it
++#ifdef WIN32
++ if ( (adhandle= pcap_open(
++ currentdev->name, // name of the device
++ 65536, // portion of the packet to capture
++ // 65536 = whole packet
++ PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
++ -1, // read timeout
++ NULL, // authentication on the remote machine
++ errbuf // error buffer
++ ) ) == NULL)
++#else
++ /*pcap_t *pcap_open_live(const char *device, int snaplen,
++ int promisc, int to_ms, char *errbuf)*/
++ if ( (adhandle= pcap_open_live(
++ currentdev->name, // name of the device
++ 65536, // portion of the packet to capture
++ // 65536 = whole packet
++ true, // promiscuous mode
++ -1, // read timeout
++ errbuf // error buffer
++ ) ) == NULL)
++
++#endif
++ {
++ LOG_MSG("\nUnable to open the interface: %s.", errbuf);
++ pcap_freealldevs(alldevs);
++ load_success = false;
++ return;
++ }
++ pcap_freealldevs(alldevs);
++#ifndef WIN32
++ pcap_setnonblock(adhandle,1,errbuf);
++#endif
++ // create the bochs NIC class
++ theNE2kDevice = new bx_ne2k_c ();
++ memcpy(theNE2kDevice->s.physaddr, mac, 6);
++ theNE2kDevice->init();
++
++ theNE2kDevice->s.base_address=base;
++ theNE2kDevice->s.base_irq=irq;
++
++ // install I/O-handlers and timer
++ for(Bitu i = 0; i < 0x20; i++) {
++ ReadHandler8[i].Install((i+theNE2kDevice->s.base_address),
++ dosbox_read,IO_MB|IO_MW);
++ WriteHandler8[i].Install((i+theNE2kDevice->s.base_address),
++ dosbox_write,IO_MB|IO_MW);
++ }
++ TIMER_AddTickHandler(NE2000_Poller);
++ }
++
++ ~NE2K() {
++ if(adhandle) pcap_close(adhandle);
++ adhandle=0;
++ if(theNE2kDevice != 0) delete theNE2kDevice;
++ theNE2kDevice=0;
++ TIMER_DelTickHandler(NE2000_Poller);
++ PIC_RemoveEvents(NE2000_TX_Event);
++ }
++};
++
++static NE2K* test;
++void NE2K_ShutDown(Section* sec) {
++ if(test) delete test;
++ test=0;
++}
++
++void NE2K_Init(Section* sec) {
++ test = new NE2K(sec);
++ sec->AddDestroyFunction(&NE2K_ShutDown,true);
++ if(!test->load_success) {
++ delete test;
++ test=0;
++ }
++}
++
++#endif // C_NE2000
+Index: src/hardware/Makefile.am
+===================================================================
+--- src/hardware/Makefile.am (revision 3661)
++++ src/hardware/Makefile.am (working copy)
+@@ -10,6 +10,6 @@
+ memory.cpp mixer.cpp pcspeaker.cpp pic.cpp sblaster.cpp tandy_sound.cpp timer.cpp \
+ vga.cpp vga_attr.cpp vga_crtc.cpp vga_dac.cpp vga_draw.cpp vga_gfx.cpp vga_other.cpp \
+ vga_memory.cpp vga_misc.cpp vga_seq.cpp vga_xga.cpp vga_s3.cpp vga_tseng.cpp vga_paradise.cpp \
+- cmos.cpp disney.cpp gus.cpp mpu401.cpp ipx.cpp ipxserver.cpp dbopl.cpp
++ cmos.cpp disney.cpp gus.cpp mpu401.cpp ipx.cpp ipxserver.cpp dbopl.cpp ne2000.cpp
+
+
+Index: src/dosbox.cpp
+===================================================================
+--- src/dosbox.cpp (revision 3661)
++++ src/dosbox.cpp (working copy)
+@@ -43,6 +43,11 @@
+ #include "ints/int10.h"
+ #include "render.h"
+
++#ifdef C_NE2000
++//#include "ne2000.h"
++void NE2K_Init(Section* sec);
++#endif
++
+ Config * control;
+ MachineType machine;
+ SVGACards svgaCard;
+@@ -680,6 +685,46 @@
+ Pbool = secprop->Add_bool("ipx",Property::Changeable::WhenIdle, false);
+ Pbool->Set_help("Enable ipx over UDP/IP emulation.");
+ #endif
++
++#ifdef C_NE2000
++ secprop=control->AddSection_prop("ne2000",&NE2K_Init,true);
++ MSG_Add("NE2000_CONFIGFILE_HELP",
++ "macaddr -- The physical address the emulator will use on your network.\n"
++ " If you have multiple DOSBoxes running on your network,\n"
++ " this has to be changed. Modify the last three number blocks.\n"
++ " I.e. AC:DE:48:88:99:AB.\n"
++ "realnic -- Specifies which of your network interfaces is used.\n"
++ " Write \'list\' here to see the list of devices in the\n"
++ " Status Window. Then make your choice and put either the\n"
++ " interface number (2 or something) or a part of your adapters\n"
++ " name, e.g. VIA here.\n"
++
++ );
++
++ Pbool = secprop->Add_bool("ne2000", Property::Changeable::WhenIdle, true);
++ Pbool->Set_help("Enable Ethernet passthrough. Requires [Win]Pcap.");
++
++ Phex = secprop->Add_hex("nicbase", Property::Changeable::WhenIdle, 0x300);
++ Phex->Set_help("The base address of the NE2000 board.");
++
++ Pint = secprop->Add_int("nicirq", Property::Changeable::WhenIdle, 3);
++ Pint->Set_help("The interrupt it uses. Note serial2 uses IRQ3 as default.");
++
++ Pstring = secprop->Add_string("macaddr", Property::Changeable::WhenIdle,"AC:DE:48:88:99:AA");
++ Pstring->Set_help("The physical address the emulator will use on your network.\n"
++ "If you have multiple DOSBoxes running on your network,\n"
++ "this has to be changed for each. AC:DE:48 is an address range reserved for\n"
++ "private use, so modify the last three number blocks.\n"
++ "I.e. AC:DE:48:88:99:AB.");
++
++ Pstring = secprop->Add_string("realnic", Property::Changeable::WhenIdle,"list");
++ Pstring->Set_help("Specifies which of your network interfaces is used.\n"
++ "Write \'list\' here to see the list of devices in the\n"
++ "Status Window. Then make your choice and put either the\n"
++ "interface number (2 or something) or a part of your adapters\n"
++ "name, e.g. VIA here.");
++#endif // C_NE2000
++
+ // secprop->AddInitFunction(&CREDITS_Init);
+
+ //TODO ?
+Index: include/ne2000.h
+===================================================================
+--- include/ne2000.h (revision 0)
++++ include/ne2000.h (revision 0)
+@@ -0,0 +1,251 @@
++/////////////////////////////////////////////////////////////////////////
++// $Id: ne2k.h,v 1.11.2.3 2003/04/06 17:29:49 bdenney Exp $
++/////////////////////////////////////////////////////////////////////////
++//
++// Copyright (C) 2001 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++// Peter Grehan (grehan@iprg.nokia.com) coded all of this
++// NE2000/ether stuff.
++
++//
++// An implementation of an ne2000 ISA ethernet adapter. This part uses
++// a National Semiconductor DS-8390 ethernet MAC chip, with some h/w
++// to provide a windowed memory region for the chip and a MAC address.
++//
++
++#include "dosbox.h"
++
++#define bx_bool int
++#define bx_param_c Bit8u
++
++
++# define BX_NE2K_SMF
++# define BX_NE2K_THIS_PTR
++# define BX_NE2K_THIS
++#define BX_INFO
++//LOG_MSG
++#define BX_DEBUG
++//LOG_MSG
++
++#define BX_NE2K_MEMSIZ (32*1024)
++#define BX_NE2K_MEMSTART (16*1024)
++#define BX_NE2K_MEMEND (BX_NE2K_MEMSTART + BX_NE2K_MEMSIZ)
++
++typedef struct {
++ //
++ // ne2k register state
++
++ //
++ // Page 0
++ //
++ // Command Register - 00h read/write
++ struct CR_t {
++ bx_bool stop; // STP - Software Reset command
++ bx_bool start; // START - start the NIC
++ bx_bool tx_packet; // TXP - initiate packet transmission
++ Bit8u rdma_cmd; // RD0,RD1,RD2 - Remote DMA command
++ Bit8u pgsel; // PS0,PS1 - Page select
++ } CR;
++ // Interrupt Status Register - 07h read/write
++ struct ISR_t {
++ bx_bool pkt_rx; // PRX - packet received with no errors
++ bx_bool pkt_tx; // PTX - packet transmitted with no errors
++ bx_bool rx_err; // RXE - packet received with 1 or more errors
++ bx_bool tx_err; // TXE - packet tx'd " " " " "
++ bx_bool overwrite; // OVW - rx buffer resources exhausted
++ bx_bool cnt_oflow; // CNT - network tally counter MSB's set
++ bx_bool rdma_done; // RDC - remote DMA complete
++ bx_bool reset; // RST - reset status
++ } ISR;
++ // Interrupt Mask Register - 0fh write
++ struct IMR_t {
++ bx_bool rx_inte; // PRXE - packet rx interrupt enable
++ bx_bool tx_inte; // PTXE - packet tx interrput enable
++ bx_bool rxerr_inte; // RXEE - rx error interrupt enable
++ bx_bool txerr_inte; // TXEE - tx error interrupt enable
++ bx_bool overw_inte; // OVWE - overwrite warn int enable
++ bx_bool cofl_inte; // CNTE - counter o'flow int enable
++ bx_bool rdma_inte; // RDCE - remote DMA complete int enable
++ bx_bool reserved; // D7 - reserved
++ } IMR;
++ // Data Configuration Register - 0eh write
++ struct DCR_t {
++ bx_bool wdsize; // WTS - 8/16-bit select
++ bx_bool endian; // BOS - byte-order select
++ bx_bool longaddr; // LAS - long-address select
++ bx_bool loop; // LS - loopback select
++ bx_bool auto_rx; // AR - auto-remove rx packets with remote DMA
++ Bit8u fifo_size; // FT0,FT1 - fifo threshold
++ } DCR;
++ // Transmit Configuration Register - 0dh write
++ struct TCR_t {
++ bx_bool crc_disable; // CRC - inhibit tx CRC
++ Bit8u loop_cntl; // LB0,LB1 - loopback control
++ bx_bool ext_stoptx; // ATD - allow tx disable by external mcast
++ bx_bool coll_prio; // OFST - backoff algorithm select
++ Bit8u reserved; // D5,D6,D7 - reserved
++ } TCR;
++ // Transmit Status Register - 04h read
++ struct TSR_t {
++ bx_bool tx_ok; // PTX - tx complete without error
++ bx_bool reserved; // D1 - reserved
++ bx_bool collided; // COL - tx collided >= 1 times
++ bx_bool aborted; // ABT - aborted due to excessive collisions
++ bx_bool no_carrier; // CRS - carrier-sense lost
++ bx_bool fifo_ur; // FU - FIFO underrun
++ bx_bool cd_hbeat; // CDH - no tx cd-heartbeat from transceiver
++ bx_bool ow_coll; // OWC - out-of-window collision
++ } TSR;
++ // Receive Configuration Register - 0ch write
++ struct RCR_t {
++ bx_bool errors_ok; // SEP - accept pkts with rx errors
++ bx_bool runts_ok; // AR - accept < 64-byte runts
++ bx_bool broadcast; // AB - accept eth broadcast address
++ bx_bool multicast; // AM - check mcast hash array
++ bx_bool promisc; // PRO - accept all packets
++ bx_bool monitor; // MON - check pkts, but don't rx
++ Bit8u reserved; // D6,D7 - reserved
++ } RCR;
++ // Receive Status Register - 0ch read
++ struct RSR_t {
++ bx_bool rx_ok; // PRX - rx complete without error
++ bx_bool bad_crc; // CRC - Bad CRC detected
++ bx_bool bad_falign; // FAE - frame alignment error
++ bx_bool fifo_or; // FO - FIFO overrun
++ bx_bool rx_missed; // MPA - missed packet error
++ bx_bool rx_mbit; // PHY - unicast or mcast/bcast address match
++ bx_bool rx_disabled; // DIS - set when in monitor mode
++ bx_bool deferred; // DFR - collision active
++ } RSR;
++
++ Bit16u local_dma; // 01,02h read ; current local DMA addr
++ Bit8u page_start; // 01h write ; page start register
++ Bit8u page_stop; // 02h write ; page stop register
++ Bit8u bound_ptr; // 03h read/write ; boundary pointer
++ Bit8u tx_page_start; // 04h write ; transmit page start register
++ Bit8u num_coll; // 05h read ; number-of-collisions register
++ Bit16u tx_bytes; // 05,06h write ; transmit byte-count register
++ Bit8u fifo; // 06h read ; FIFO
++ Bit16u remote_dma; // 08,09h read ; current remote DMA addr
++ Bit16u remote_start; // 08,09h write ; remote start address register
++ Bit16u remote_bytes; // 0a,0bh write ; remote byte-count register
++ Bit8u tallycnt_0; // 0dh read ; tally counter 0 (frame align errors)
++ Bit8u tallycnt_1; // 0eh read ; tally counter 1 (CRC errors)
++ Bit8u tallycnt_2; // 0fh read ; tally counter 2 (missed pkt errors)
++
++ //
++ // Page 1
++ //
++ // Command Register 00h (repeated)
++ //
++ Bit8u physaddr[6]; // 01-06h read/write ; MAC address
++ Bit8u curr_page; // 07h read/write ; current page register
++ Bit8u mchash[8]; // 08-0fh read/write ; multicast hash array
++
++ //
++ // Page 2 - diagnostic use only
++ //
++ // Command Register 00h (repeated)
++ //
++ // Page Start Register 01h read (repeated)
++ // Page Stop Register 02h read (repeated)
++ // Current Local DMA Address 01,02h write (repeated)
++ // Transmit Page start address 04h read (repeated)
++ // Receive Configuration Register 0ch read (repeated)
++ // Transmit Configuration Register 0dh read (repeated)
++ // Data Configuration Register 0eh read (repeated)
++ // Interrupt Mask Register 0fh read (repeated)
++ //
++ Bit8u rempkt_ptr; // 03h read/write ; remote next-packet pointer
++ Bit8u localpkt_ptr; // 05h read/write ; local next-packet pointer
++ Bit16u address_cnt; // 06,07h read/write ; address counter
++
++ //
++ // Page 3 - should never be modified.
++ //
++
++ // Novell ASIC state
++ Bit8u macaddr[32]; // ASIC ROM'd MAC address, even bytes
++ Bit8u mem[BX_NE2K_MEMSIZ]; // on-chip packet memory
++
++ // ne2k internal state
++ Bit32u base_address;
++ int base_irq;
++ int tx_timer_index;
++ int tx_timer_active;
++
++ void register_state(bx_param_c *list_p);
++
++} bx_ne2k_t;
++
++
++
++class bx_ne2k_c {
++public:
++ bx_ne2k_c(void);
++ ~bx_ne2k_c(void);
++ virtual void init(void);
++ virtual void reset(unsigned type);
++
++public:
++ bx_ne2k_t s;
++
++ /* TODO: Setup SDL */
++ //eth_pktmover_c *ethdev;
++
++ BX_NE2K_SMF Bit32u read_cr(void);
++ BX_NE2K_SMF void write_cr(Bit32u value);
++
++ BX_NE2K_SMF Bit32u chipmem_read(Bit32u address, unsigned io_len);
++ BX_NE2K_SMF Bit32u asic_read(Bit32u offset, unsigned io_len);
++ BX_NE2K_SMF Bit32u page0_read(Bit32u offset, unsigned io_len);
++ BX_NE2K_SMF Bit32u page1_read(Bit32u offset, unsigned io_len);
++ BX_NE2K_SMF Bit32u page2_read(Bit32u offset, unsigned io_len);
++ BX_NE2K_SMF Bit32u page3_read(Bit32u offset, unsigned io_len);
++
++ BX_NE2K_SMF void chipmem_write(Bit32u address, Bit32u value, unsigned io_len);
++ BX_NE2K_SMF void asic_write(Bit32u address, Bit32u value, unsigned io_len);
++ BX_NE2K_SMF void page0_write(Bit32u address, Bit32u value, unsigned io_len);
++ BX_NE2K_SMF void page1_write(Bit32u address, Bit32u value, unsigned io_len);
++ BX_NE2K_SMF void page2_write(Bit32u address, Bit32u value, unsigned io_len);
++ BX_NE2K_SMF void page3_write(Bit32u address, Bit32u value, unsigned io_len);
++
++public:
++ static void tx_timer_handler(void *);
++ BX_NE2K_SMF void tx_timer(void);
++
++ static void rx_handler(void *arg, const void *buf, unsigned len);
++ BX_NE2K_SMF unsigned mcast_index(const void *dst);
++ BX_NE2K_SMF void rx_frame(const void *buf, unsigned io_len);
++
++
++ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
++ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
++#if !BX_USE_NE2K_SMF
++ Bit32u read(Bit32u address, unsigned io_len);
++ void write(Bit32u address, Bit32u value, unsigned io_len);
++#endif
++
++
++};
++
+Index: include/Makefile.am
+===================================================================
+--- include/Makefile.am (revision 3661)
++++ include/Makefile.am (working copy)
+@@ -35,5 +35,6 @@
+ support.h \
+ timer.h \
+ vga.h \
+-video.h
++video.h \
++ne2000.h
+
diff --git a/src/dosbox.cpp b/src/dosbox.cpp
index 1462fae..5d5d370 100644
--- a/src/dosbox.cpp
+++ b/src/dosbox.cpp
@@ -43,6 +43,11 @@
#include "render.h"
#include "pci_bus.h"
+#ifdef C_NE2000
+//#include "ne2000.h"
+void NE2K_Init(Section* sec);
+#endif
+
Config * control;
MachineType machine;
SVGACards svgaCard;
@@ -705,6 +710,46 @@ void DOSBOX_Init(void) {
Pbool = secprop->Add_bool("ipx",Property::Changeable::WhenIdle, false);
Pbool->Set_help("Enable ipx over UDP/IP emulation.");
#endif
+
+#ifdef C_NE2000
+ secprop=control->AddSection_prop("ne2000",&NE2K_Init,true);
+ MSG_Add("NE2000_CONFIGFILE_HELP",
+ "macaddr -- The physical address the emulator will use on your network.\n"
+ " If you have multiple DOSBoxes running on your network,\n"
+ " this has to be changed. Modify the last three number blocks.\n"
+ " I.e. AC:DE:48:88:99:AB.\n"
+ "realnic -- Specifies which of your network interfaces is used.\n"
+ " Write \'list\' here to see the list of devices in the\n"
+ " Status Window. Then make your choice and put either the\n"
+ " interface number (2 or something) or a part of your adapters\n"
+ " name, e.g. VIA here.\n"
+
+ );
+
+ Pbool = secprop->Add_bool("ne2000", Property::Changeable::WhenIdle, true);
+ Pbool->Set_help("Enable Ethernet passthrough. Requires [Win]Pcap.");
+
+ Phex = secprop->Add_hex("nicbase", Property::Changeable::WhenIdle, 0x300);
+ Phex->Set_help("The base address of the NE2000 board.");
+
+ Pint = secprop->Add_int("nicirq", Property::Changeable::WhenIdle, 3);
+ Pint->Set_help("The interrupt it uses. Note serial2 uses IRQ3 as default.");
+
+ Pstring = secprop->Add_string("macaddr", Property::Changeable::WhenIdle,"AC:DE:48:88:99:AA");
+ Pstring->Set_help("The physical address the emulator will use on your network.\n"
+ "If you have multiple DOSBoxes running on your network,\n"
+ "this has to be changed for each. AC:DE:48 is an address range reserved for\n"
+ "private use, so modify the last three number blocks.\n"
+ "I.e. AC:DE:48:88:99:AB.");
+
+ Pstring = secprop->Add_string("realnic", Property::Changeable::WhenIdle,"list");
+ Pstring->Set_help("Specifies which of your network interfaces is used.\n"
+ "Write \'list\' here to see the list of devices in the\n"
+ "Status Window. Then make your choice and put either the\n"
+ "interface number (2 or something) or a part of your adapters\n"
+ "name, e.g. VIA here.");
+#endif // C_NE2000
+
// secprop->AddInitFunction(&CREDITS_Init);
//TODO ?
diff --git a/src/hardware/Makefile.am b/src/hardware/Makefile.am
index 1ec7e67..0666a3f 100644
--- a/src/hardware/Makefile.am
+++ b/src/hardware/Makefile.am
@@ -10,6 +10,6 @@ libhardware_a_SOURCES = adlib.cpp dma.cpp gameblaster.cpp hardware.cpp iohandler
memory.cpp mixer.cpp pcspeaker.cpp pci_bus.cpp pic.cpp sblaster.cpp tandy_sound.cpp timer.cpp \
vga.cpp vga_attr.cpp vga_crtc.cpp vga_dac.cpp vga_draw.cpp vga_gfx.cpp vga_other.cpp \
vga_memory.cpp vga_misc.cpp vga_seq.cpp vga_xga.cpp vga_s3.cpp vga_tseng.cpp vga_paradise.cpp \
- cmos.cpp disney.cpp gus.cpp mpu401.cpp ipx.cpp ipxserver.cpp dbopl.cpp
+ cmos.cpp disney.cpp gus.cpp mpu401.cpp ipx.cpp ipxserver.cpp dbopl.cpp ne2000.cpp
diff --git a/src/hardware/ne2000.cpp b/src/hardware/ne2000.cpp
new file mode 100644
index 0000000..e75cf8d
--- /dev/null
+++ b/src/hardware/ne2000.cpp
@@ -0,0 +1,1723 @@
+#include "config.h"
+
+#ifdef C_NE2000
+
+
+#include "dosbox.h"
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "support.h"
+#include "inout.h"
+#include "setup.h"
+#include "callback.h"
+#include "timer.h"
+#include "pic.h"
+#include "cpu.h"
+
+/* Couldn't find a real spec for the NE2000 out there, hence this is adapted heavily from Bochs */
+
+
+/////////////////////////////////////////////////////////////////////////
+// $Id: ne2k.cc,v 1.56.2.1 2004/02/02 22:37:22 cbothamy 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// Peter Grehan (grehan@iprg.nokia.com) coded all of this
+// NE2000/ether stuff.
+
+// Kristian Gunstone added dodgy suid code, and minor adjustments to
+// make Peter's 2010(ne2000_27_11_10.diff) patch build on a 2015(r3950)
+// codebase.
+
+#include "ne2000.h"
+
+#define HAVE_REMOTE
+
+#include "pcap.h"
+
+// Placeholder for privelege drop/escalation
+uid_t orig_uid;
+
+void NE2000_PrivelegeEscalate(void);
+void NE2000_PrivelegeDrop(void);
+
+// Handle to WinPCap device
+pcap_t *adhandle = 0;
+static void NE2000_TX_Event(Bitu val);
+
+#ifdef WIN32
+// DLL loading
+#define pcap_sendpacket(A,B,C) PacketSendPacket(A,B,C)
+#define pcap_close(A) PacketClose(A)
+#define pcap_freealldevs(A) PacketFreealldevs(A)
+#define pcap_open(A,B,C,D,E,F) PacketOpen(A,B,C,D,E,F)
+#define pcap_next_ex(A,B,C) PacketNextEx(A,B,C)
+#define pcap_findalldevs_ex(A,B,C,D) PacketFindALlDevsEx(A,B,C,D)
+
+int (*PacketSendPacket)(pcap_t *, const u_char *, int) = 0;
+void (*PacketClose)(pcap_t *) = 0;
+void (*PacketFreealldevs)(pcap_if_t *) = 0;
+pcap_t* (*PacketOpen)(char const *,int,int,int,struct pcap_rmtauth *,char *) = 0;
+int (*PacketNextEx)(pcap_t *, struct pcap_pkthdr **, const u_char **) = 0;
+int (*PacketFindALlDevsEx)(char *, struct pcap_rmtauth *, pcap_if_t **, char *) = 0;
+
+#endif
+
+//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->
+//#define BX_DEBUG
+//#define BX_INFO
+#define BX_NULL_TIMER_HANDLE NULL
+#define BX_PANIC
+#define BX_ERROR
+#define BX_RESET_HARDWARE 0
+#define BX_RESET_SOFTWARE 1
+
+bx_ne2k_c* theNE2kDevice = NULL;
+
+
+bx_ne2k_c::bx_ne2k_c(void)
+{
+ s.tx_timer_index = BX_NULL_TIMER_HANDLE;
+}
+
+
+bx_ne2k_c::~bx_ne2k_c(void)
+{
+ // nothing for now
+}
+
+//
+// reset - restore state to power-up, cancelling all i/o
+//
+void
+bx_ne2k_c::reset(unsigned type)
+{
+ BX_DEBUG ("reset");
+ // 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.tx_timer_active = 0;
+ 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.ISR.reset = 1;
+ BX_NE2K_THIS s.DCR.longaddr = 1;
+ PIC_DeActivateIRQ(s.base_irq);
+ //DEV_pic_lower_irq(BX_NE2K_THIS s.base_irq);
+}
+
+//
+// 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%08x", 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 */
+ //value = 0x22; /* 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) {
+ // loopback mode
+ 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);
+
+ // do a TX interrupt
+ // 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) {
+ //LOG_MSG("tx complete interrupt");
+ PIC_ActivateIRQ(s.base_irq);
+ }
+ BX_NE2K_THIS s.ISR.pkt_tx = 1;
+ }
+ } else if (value & 0x04) {
+ // start-tx and no loopback
+ if (BX_NE2K_THIS s.CR.stop || !BX_NE2K_THIS s.CR.start)
+ 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"));
+
+#ifdef notdef
+ // XXX debug stuff
+ printf("packet tx (%d bytes):\t", BX_NE2K_THIS s.tx_bytes);
+ 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))
+ printf("\t");
+ }
+ printf("");
+#endif
+
+ // Send the packet to the system driver
+ /* TODO: Transmit packet */
+ //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);
+ NE2000_PrivelegeEscalate();
+ pcap_sendpacket(adhandle,&s.mem[s.tx_page_start*256 - BX_NE2K_MEMSTART], s.tx_bytes);
+ NE2000_PrivelegeDrop();
+
+ // some more debug
+ if (BX_NE2K_THIS s.tx_timer_active) {
+ BX_PANIC(("CR write, tx timer still active"));
+ PIC_RemoveEvents(NE2000_TX_Event);
+ }
+ //LOG_MSG("send packet command");
+ //s.tx_timer_index = (64 + 96 + 4*8 + BX_NE2K_THIS s.tx_bytes*8)/10;
+ s.tx_timer_active = 1;
+ PIC_AddEvent(NE2000_TX_Event,(float)((64 + 96 + 4*8 + BX_NE2K_THIS s.tx_bytes*8)/10000.0),0);
+ // 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).
+ //
+
+ /* TODO: Code transmit timer */
+ /*
+ 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
+ */
+ } // end transmit-start branch
+
+ // 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) {
+ PIC_ActivateIRQ(s.base_irq);
+ //DEV_pic_raise_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))
+ 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_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)
+ BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 1] = value >> 8;
+ } 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_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).
+ //
+ 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 > 1)
+ 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) {
+ PIC_ActivateIRQ(s.base_irq);
+ //DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
+ }
+ }
+ break;
+
+ case 0xf: // Reset register
+ theNE2kDevice->reset(BX_RESET_SOFTWARE);
+ //retval=0x1;
+ 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 == 2) && (BX_NE2K_THIS s.DCR.wdsize == 0)) {
+ BX_PANIC(("dma write length 2 on byte mode operation"));
+ break;
+ }
+
+ if (BX_NE2K_THIS s.remote_bytes == 0)
+ BX_PANIC(("ne2K: dma write, byte count 0"));
+
+ chipmem_write(BX_NE2K_THIS s.remote_dma, value, io_len);
+ // is this right ??? asic_read uses DCR.wordsize
+ BX_NE2K_THIS s.remote_dma += io_len;
+ 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;
+ }
+
+ BX_NE2K_THIS s.remote_bytes -= io_len;
+ 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) {
+ PIC_ActivateIRQ(s.base_irq);
+ //DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
+ }
+ }
+ break;
+
+ case 0xf: // Reset register
+ theNE2kDevice->reset(BX_RESET_SOFTWARE);
+ 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)
+{
+ BX_DEBUG("page 0 read from port %04x, len=%u", (unsigned) offset,
+ (unsigned) io_len);
+ 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;
+ }
+
+
+ switch (offset) {
+ 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
+ // reading FIFO is only valid in loopback mode
+ BX_ERROR(("reading FIFO not supported yet"));
+ 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
+ BX_INFO(("reserved read - page 0, 0xa"));
+ return (0xff);
+ break;
+
+ case 0xb: // reserved
+ BX_INFO(("reserved read - page 0, 0xb"));
+ 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:
+ BX_PANIC("page 0 offset %04x out of range", (unsigned) offset);
+ }
+
+ return(0);
+}
+
+void
+bx_ne2k_c::page0_write(Bit32u offset, Bit32u value, unsigned io_len)
+{
+ BX_DEBUG("page 0 write to port %04x, len=%u", (unsigned) offset,
+ (unsigned) io_len);
+
+ // 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);
+ page0_write(offset + 1, ((value >> 8) & 0xff), 1);
+ return;
+ }
+
+ switch (offset) {
+ case 0x1: // PSTART
+ BX_NE2K_THIS s.page_start = value;
+ break;
+
+ case 0x2: // PSTOP
+ // BX_INFO(("Writing to PSTOP: %02x", value));
+ 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)
+ PIC_DeActivateIRQ(s.base_irq);
+ //DEV_pic_lower_irq(BX_NE2K_THIS s.base_irq);
+ 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_PANIC(("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);
+ if(BX_NE2K_THIS s.ISR.pkt_tx && BX_NE2K_THIS s.IMR.tx_inte) {
+ LOG_MSG("tx irq retrigger");
+ PIC_ActivateIRQ(s.base_irq);
+ }
+ break;
+ default:
+ BX_PANIC("page 0 write, bad offset %0x", 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 port %04x, len=%u", (unsigned) offset,
+ (unsigned) io_len);
+ if (io_len > 1)
+ BX_PANIC("bad length! page 1 read from port %04x, len=%u", (unsigned) offset,
+ (unsigned) 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: %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 r offset %04x out of range", (unsigned) offset);
+ }
+
+ return (0);
+}
+
+void
+bx_ne2k_c::page1_write(Bit32u offset, Bit32u value, unsigned io_len)
+{
+ BX_DEBUG("page 1 w offset %04x", (unsigned) offset);
+ switch (offset) {
+ 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:
+ BX_PANIC("page 1 w offset %04x out of range", (unsigned) 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 port %04x, len=%u", (unsigned) offset, (unsigned) io_len);
+
+ if (io_len > 1)
+ BX_PANIC("bad length! page 2 read from port %04x, len=%u", (unsigned) offset, (unsigned) io_len);
+
+ switch (offset) {
+ 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:
+ BX_ERROR("reserved read - page 2, 0x%02x", (unsigned) offset);
+ 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:
+ BX_PANIC("page 2 offset %04x out of range", (unsigned) 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.
+ if (offset != 0)
+ BX_ERROR(("page 2 write ?"));
+
+ 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 offset 4"));
+ 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 offset %0x", offset);
+ break;
+
+ default:
+ BX_PANIC("page 2 write, illegal offset %0x", offset);
+ break;
+ }
+}
+
+//
+// page3_read/page3_write - writes to this page are illegal
+//
+Bit32u
+bx_ne2k_c::page3_read(Bit32u offset, unsigned int io_len)
+{
+ BX_PANIC(("page 3 read attempted"));
+ return (0);
+}
+
+void
+bx_ne2k_c::page3_write(Bit32u offset, Bit32u value, unsigned io_len)
+{
+ BX_PANIC(("page 3 write attempted"));
+}
+
+//
+// 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.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) {
+ //LOG_MSG("tx complete interrupt");
+ PIC_ActivateIRQ(s.base_irq);
+ //DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
+ } //else LOG_MSG("no tx complete interrupt");
+ BX_NE2K_THIS s.ISR.pkt_tx = 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 with length %d", 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
+ 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)
+{
+ // BX_DEBUG(("rx_handler with length %d", len));
+ bx_ne2k_c *class_ptr = (bx_ne2k_c *) arg;
+ if(
+ 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;
+ static unsigned char bcast_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
+
+ if(io_len != 60) {
+ BX_DEBUG("rx_frame with length %d", io_len);
+ }
+
+ //LOG_MSG("stop=%d, pagestart=%x, dcr_loop=%x, tcr_loopcntl=%x",
+ // BX_NE2K_THIS s.CR.stop, BX_NE2K_THIS s.page_start,
+ // BX_NE2K_THIS s.DCR.loop, BX_NE2K_THIS s.TCR.loop_cntl);
+ 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
+ ) {
+ BX_DEBUG("no space");
+ return;
+ }
+
+ if ((io_len < 40/*60*/) && !BX_NE2K_THIS s.RCR.runts_ok) {
+ BX_DEBUG("rejected small packet, length %d", io_len);
+ return;
+ }
+ // some computers don't care...
+ if (io_len < 60) io_len=60;
+
+ // Do address filtering if not in promiscuous mode
+#if 0
+ if (! BX_NE2K_THIS s.RCR.promisc) {
+ BX_DEBUG("NOT in promiscuous mode, apparently");
+ if (!memcmp(buf, bcast_addr, 6)) {
+ if (!BX_NE2K_THIS s.RCR.broadcast) {
+ return;
+ }
+ } else if (pktbuf[0] & 0x01) {
+ if (! BX_NE2K_THIS s.RCR.multicast) {
+ return;
+ }
+ BX_DEBUG("mcast_index\n");
+ idx = mcast_index(buf);
+ BX_DEBUG("BX_NE2K_THIS: idx = %d", idx);
+ 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"));
+ }
+
+#else
+ BX_DEBUG("rx_frame: Ignoring promiscuous mode check; it seems broken!");
+ BX_DEBUG("rx_frame: Blindly assuming all is OK, expect fire");
+ BX_DEBUG(("rx_frame promiscuous receive"));
+#endif
+
+ 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;
+ 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) {
+ //LOG_MSG("packet rx interrupt");
+ PIC_ActivateIRQ(s.base_irq);
+ //DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
+ } //else LOG_MSG("no packet rx interrupt");
+
+}
+
+//Bit8u macaddr[6] = { 0xAC, 0xDE, 0x48, 0x8E, 0x89, 0x19 };
+
+Bitu dosbox_read(Bitu port, Bitu len) {
+ Bitu retval = theNE2kDevice->read(port,len);
+ //LOG_MSG("ne2k rd port %x val %4x len %d page %d, CS:IP %8x:%8x",
+ // port, retval, len, theNE2kDevice->s.CR.pgsel,SegValue(cs),reg_eip);
+ return retval;
+}
+void dosbox_write(Bitu port, Bitu val, Bitu len) {
+ //LOG_MSG("ne2k wr port %x val %4x len %d page %d, CS:IP %8x:%8x",
+ // port, val, len,theNE2kDevice->s.CR.pgsel,SegValue(cs),reg_eip);
+ theNE2kDevice->write(port, val, len);
+}
+
+void bx_ne2k_c::init()
+{
+ //BX_DEBUG(("Init $Id: ne2k.cc,v 1.56.2.1 2004/02/02 22:37:22 cbothamy Exp $"));
+
+ // Read in values from config file
+ //BX_NE2K_THIS s.base_address = 0x300;
+ //BX_NE2K_THIS s.base_irq = 3;
+ /*
+ 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
+ //DEV_register_irq(BX_NE2K_THIS s.base_irq, "NE2000 ethernet NIC");
+
+ //DEV_register_ioread_handler(this, read_handler, addr, "ne2000 NIC", 3);
+ //DEV_register_iowrite_handler(this, write_handler, addr, "ne2000 NIC", 3);
+
+
+ orig_uid = getuid();
+
+ /*
+ LOG_MSG("Original uid, euid, gid: %d, %d, %d",
+ orig_uid,
+ orig_euid,
+ orig_gid);
+ */
+ 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,
+ 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]);
+
+ // 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 (Bitu i = 12; i < 32; i++)
+ BX_NE2K_THIS s.macaddr[i] = 0x57;
+
+ // Bring the register state into power-up state
+ reset(BX_RESET_HARDWARE);
+}
+
+void NE2000_PrivelegeEscalate(void)
+{
+#ifndef WIN32
+ // LOG_MSG("NE2000: privelege escalate");
+ if(setuid(0) != 0) {
+ LOG_MSG("NE2000: setuid(%d) failed", 0);
+ }
+#endif
+}
+
+void NE2000_PrivelegeDrop(void)
+{
+#ifndef WIN32
+ //LOG_MSG("NE2000: privelege drop");
+ if(setuid(orig_uid) != 0) {
+ LOG_MSG("NE2000: setuid(%d) failed", orig_uid);
+ }
+#endif
+}
+
+static void NE2000_TX_Event(Bitu val) {
+ theNE2kDevice->tx_timer();
+}
+
+static void NE2000_Poller(void) {
+ int res;
+ struct pcap_pkthdr *header;
+ u_char *pkt_data;
+ NE2000_PrivelegeEscalate();
+//#if 0
+ while((res = pcap_next_ex( adhandle, &header, (const u_char **)&pkt_data)) > 0) {
+ //LOG_MSG("NE2000: Received %d bytes", header->len);
+
+ // don't receive in loopback modes
+ if((theNE2kDevice->s.DCR.loop == 0) || (theNE2kDevice->s.TCR.loop_cntl != 0)) {
+ NE2000_PrivelegeDrop();
+ return;
+ }
+ theNE2kDevice->rx_frame(pkt_data, header->len);
+ }
+ NE2000_PrivelegeDrop();
+//#endif
+}
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+class NE2K: public Module_base {
+private:
+ // Data
+ IO_ReadHandleObject ReadHandler8[0x20];
+ IO_WriteHandleObject WriteHandler8[0x20];
+ IO_ReadHandleObject ReadHandler16[0x10];
+ IO_WriteHandleObject WriteHandler16[0x10];
+
+public:
+ bool load_success;
+ NE2K(Section* configuration):Module_base(configuration) {
+ Section_prop * section=static_cast<Section_prop *>(configuration);
+
+ load_success = true;
+ // enabled?
+
+ if(!section->Get_bool("ne2000")) {
+ load_success = false;
+ return;
+ }
+
+#ifdef WIN32
+/*
+ int (*PacketSendPacket)(pcap_t *, const u_char *, int);
+ void (*PacketClose)(pcap_t *);
+ void (*PacketFreealldevs)(pcap_if_t *);
+ pcap_t* (*PacketOpen)(char const *,int,int,int,struct pcap_rmtauth *,char *);
+ int (*PacketNextEx)(pcap_t *, struct pcap_pkthdr **, const u_char **);
+ int (*PacketFindALlDevsEx)(char *, struct pcap_rmtauth *, pcap_if_t **, char *);
+*/
+ // init the library
+ HINSTANCE pcapinst;
+ pcapinst = LoadLibrary("WPCAP.DLL");
+ if(pcapinst==NULL) {
+ LOG_MSG("WinPcap has to be installed for the NE2000 to work.");
+ load_success = false;
+ return;
+ }
+ FARPROC psp;
+
+ psp = GetProcAddress(pcapinst,"pcap_sendpacket");
+ if(!PacketSendPacket) PacketSendPacket =
+ (int (__cdecl *)(pcap_t *,const u_char *,int))psp;
+
+ psp = GetProcAddress(pcapinst,"pcap_close");
+ if(!PacketClose) PacketClose =
+ (void (__cdecl *)(pcap_t *)) psp;
+
+ psp = GetProcAddress(pcapinst,"pcap_freealldevs");
+ if(!PacketFreealldevs) PacketFreealldevs =
+ (void (__cdecl *)(pcap_if_t *)) psp;
+
+ psp = GetProcAddress(pcapinst,"pcap_open");
+ if(!PacketOpen) PacketOpen =
+ (pcap_t* (__cdecl *)(char const *,int,int,int,struct pcap_rmtauth *,char *)) psp;
+
+ psp = GetProcAddress(pcapinst,"pcap_next_ex");
+ if(!PacketNextEx) PacketNextEx =
+ (int (__cdecl *)(pcap_t *, struct pcap_pkthdr **, const u_char **)) psp;
+
+ psp = GetProcAddress(pcapinst,"pcap_findalldevs_ex");
+ if(!PacketFindALlDevsEx) PacketFindALlDevsEx =
+ (int (__cdecl *)(char *, struct pcap_rmtauth *, pcap_if_t **, char *)) psp;
+
+ if(PacketFindALlDevsEx==0 || PacketNextEx==0 || PacketOpen==0 ||
+ PacketFreealldevs==0 || PacketClose==0 || PacketSendPacket==0) {
+ LOG_MSG("Wrong WinPcap version or something");
+ load_success = false;
+ return;
+ }
+
+#endif
+
+ // get irq and base
+ Bitu irq = section->Get_int("nicirq");
+ if(!(irq==3 || irq==4 || irq==5 || irq==6 ||irq==7 ||
+ irq==9 || irq==10 || irq==11 || irq==12 ||irq==14 ||irq==15)) {
+ irq=3;
+ }
+ Bitu base = section->Get_hex("nicbase");
+ if(!(base==0x260||base==0x280||base==0x300||base==0x320||base==0x340||base==0x380)) {
+ base=0x300;
+ }
+
+ // mac address
+ const char* macstring=section->Get_string("macaddr");
+ Bitu macint[6];
+ Bit8u mac[6];
+ if(sscanf(macstring,"%02x:%02x:%02x:%02x:%02x:%02x",
+ &macint[0],&macint[1],&macint[2],&macint[3],&macint[4],&macint[5]) != 6) {
+ mac[0]=0xac;mac[1]=0xde;mac[2]=0x48;
+ mac[3]=0x88;mac[4]=0xbb;mac[5]=0xaa;
+ } else {
+ mac[0]=macint[0]; mac[1]=macint[1];
+ mac[2]=macint[2]; mac[3]=macint[3];
+ mac[4]=macint[4]; mac[5]=macint[5];
+ }
+
+ // find out which pcap device to use
+ const char* realnicstring=section->Get_string("realnic");
+ pcap_if_t *alldevs;
+ pcap_if_t *currentdev = NULL;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ Bitu userdev;
+#ifdef WIN32
+ if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
+#else
+ if (pcap_findalldevs(&alldevs, errbuf) == -1)
+#endif
+ {
+ LOG_MSG("Cannot enumerate network interfaces: %s\n", errbuf);
+ load_success = false;
+ return;
+ }
+ if (!strcasecmp(realnicstring,"list")) {
+ // print list and quit
+ Bitu i = 0;
+ LOG_MSG("\nNetwork Interface List \n-----------------------------------");
+ for(currentdev=alldevs; currentdev!=NULL; currentdev=currentdev->next) {
+ const char* desc = "no description";
+ if(currentdev->description) desc=currentdev->description;
+ i++;
+ LOG_MSG("%2d. %s\n (%s)\n",i,currentdev->name,desc);
+ }
+ pcap_freealldevs(alldevs);
+ load_success = false;
+ return;
+ } else if(1==sscanf(realnicstring,"%u",&userdev)) {
+ // user passed us a number
+ Bitu i = 0;
+ currentdev=alldevs;
+ while(currentdev!=NULL) {
+ i++;
+ if(i==userdev) break;
+ else currentdev=currentdev->next;
+ }
+ } else {
+ // user might have passed a piece of name
+ for(currentdev=alldevs; currentdev!=NULL; currentdev=currentdev->next) {
+ if(strstr(currentdev->name,realnicstring)) {
+ break;
+ }else if(currentdev->description!=NULL &&
+ strstr(currentdev->description,realnicstring)) {
+ break;
+ }
+ }
+ }
+
+ if(currentdev==NULL) {
+ LOG_MSG("Unable to find network interface - check realnic parameter\n");
+ load_success = false;
+ pcap_freealldevs(alldevs);
+ return;
+ }
+ // print out which interface we are going to use
+ const char* desc = "no description";
+ if(currentdev->description) desc=currentdev->description;
+ LOG_MSG("Using Network interface:\n%s\n(%s)\n",currentdev->name,desc);
+
+ NE2000_PrivelegeEscalate();
+
+ // attempt to open it
+#ifdef WIN32
+ if ( (adhandle= pcap_open(
+ currentdev->name, // name of the device
+ 65536, // portion of the packet to capture
+ // 65536 = whole packet
+ PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
+ -1, // read timeout
+ NULL, // authentication on the remote machine
+ errbuf // error buffer
+ ) ) == NULL)
+#else
+ /*pcap_t *pcap_open_live(const char *device, int snaplen,
+ int promisc, int to_ms, char *errbuf)*/
+ if ( (adhandle= pcap_open_live(
+ currentdev->name, // name of the device
+ 65536, // portion of the packet to capture
+ // 65536 = whole packet
+ true, // promiscuous mode
+ -1, // read timeout
+ errbuf // error buffer
+ ) ) == NULL)
+
+#endif
+ {
+ LOG_MSG("\nUnable to open the interface: %s.", errbuf);
+ pcap_freealldevs(alldevs);
+ load_success = false;
+ NE2000_PrivelegeDrop();
+ return;
+ }
+ pcap_freealldevs(alldevs);
+#ifndef WIN32
+ pcap_setnonblock(adhandle,1,errbuf);
+#endif
+ // create the bochs NIC class
+ theNE2kDevice = new bx_ne2k_c ();
+ memcpy(theNE2kDevice->s.physaddr, mac, 6);
+ theNE2kDevice->init();
+
+ theNE2kDevice->s.base_address=base;
+ theNE2kDevice->s.base_irq=irq;
+
+ // install I/O-handlers and timer
+ for(Bitu i = 0; i < 0x20; i++) {
+ ReadHandler8[i].Install((i+theNE2kDevice->s.base_address),
+ dosbox_read,IO_MB|IO_MW);
+ WriteHandler8[i].Install((i+theNE2kDevice->s.base_address),
+ dosbox_write,IO_MB|IO_MW);
+ }
+
+ NE2000_PrivelegeDrop();
+
+ TIMER_AddTickHandler(NE2000_Poller);
+ }
+
+ ~NE2K() {
+ if(adhandle) pcap_close(adhandle);
+ adhandle=0;
+ if(theNE2kDevice != 0) delete theNE2kDevice;
+ theNE2kDevice=0;
+ TIMER_DelTickHandler(NE2000_Poller);
+ PIC_RemoveEvents(NE2000_TX_Event);
+ }
+};
+
+static NE2K* test;
+void NE2K_ShutDown(Section* sec) {
+ if(test) delete test;
+ test=0;
+}
+
+void NE2K_Init(Section* sec) {
+ test = new NE2K(sec);
+ sec->AddDestroyFunction(&NE2K_ShutDown,true);
+ if(!test->load_success) {
+ delete test;
+ test=0;
+ }
+}
+
+#endif // C_NE2000
diff --git a/src/platform/visualc/config.h b/src/platform/visualc/config.h
index fffd256..c037a79 100644
--- a/src/platform/visualc/config.h
+++ b/src/platform/visualc/config.h
@@ -12,6 +12,9 @@
/* Define to 1 to enable internal modem support, requires SDL_net */
#define C_MODEM 1
+/* Define to 1 to enable NE2000 ethernet passthrough, requires libpcap */
+#define C_NE2000 1
+
/* Define to 1 to enable IPX networking support, requires SDL_net */
#define C_IPX 1
diff --git a/visualc_net/dosbox.vcproj b/visualc_net/dosbox.vcproj
index ae88553..88506b9 100644
--- a/visualc_net/dosbox.vcproj
+++ b/visualc_net/dosbox.vcproj
@@ -480,6 +480,9 @@
<File
RelativePath="..\src\hardware\mixer.cpp">
</File>
+ <File
+ RelativePath="..\src\hardware\ne2000.cpp">
+ </File>
<File
RelativePath="..\src\hardware\pci_bus.cpp">
</File>
@@ -834,6 +837,9 @@
<File
RelativePath="..\include\mouse.h">
</File>
+ <File
+ RelativePath="..\include\ne2000.h">
+ </File>
<File
RelativePath="..\include\paging.h">
</File>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment