Created
October 27, 2015 23:27
-
-
Save gammy/99b17dae786466abfcf7 to your computer and use it in GitHub Desktop.
An updated patch for NE2000 emulation in dosbox. More to follow.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ([email protected]) 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 ([email protected]) 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 ([email protected]) 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 ([email protected]) 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