--- Makefile.target
+++ Makefile.target
@@ -436,6 +436,12 @@ VL_OBJS += ne2000.o
 VL_OBJS += pcnet.o
 VL_OBJS += rtl8139.o
 
+# Arcnet ISA card
+ifdef CONFIG_ARCNET
+VL_OBJS+=arcnet.o
+VL_OBJS += com90c65.o
+endif
+
 ifeq ($(TARGET_BASE_ARCH), i386)
 # Hardware support
 VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
--- /dev/null
+++ arcnet.c
@@ -0,0 +1,176 @@
+/* This is a layer/tunnel which connects
+ * an emulated arcnet device and a Linux arcnet interface of the host.
+ *
+ * It communicates with the upper layer (the emulated device) by a Qemu vlan
+ * and with the bottom layer (the real device) by a raw socket.
+ * It converts packets between the real format and the Linux one.
+ * A send comes from the vlan to the socket.
+ * A receive comes from the socket to the vlan.
+ */
+
+#include "arcnet.h"
+
+#include "net.h"
+#include "qemu-char.h"
+
+#include <err.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if_arp.h>
+#include <linux/if_arcnet.h>
+
+#ifdef DEBUG
+	/* set it to 1 to print send/receive events */
+	#define DEBUG_RXTX 0
+	/* set it to 1 to print sent/received packets */
+	#define DEBUG_DUMP 0
+#else
+	#define DEBUG_RXTX 0
+	#define DEBUG_DUMP 0
+#endif
+
+#ifdef DEBUG
+	#define dprintf0(fmt, args...) fprintf (stderr, fmt, ## args)
+#else
+	#define dprintf0(fmt, args...)
+#endif
+#define dprintf(fmt, args...) dprintf0 ("arcnet: " fmt, ## args)
+#define dperror(fmt, args...) fprintf (stderr, "%s: " fmt "ERROR %d: %s\n", __FUNCTION__, ## args, errno, strerror (errno))
+
+typedef struct archdr ArcnetPacket ;
+
+/* arcnet connection between the emulator and the host interface */
+typedef struct ArcnetState {
+	VLANClientState *vlan_client ;
+	int socket ;
+	struct sockaddr address ;
+} ArcnetState ;
+
+/* send a packet from the emulator vlan to the host */
+static void arcnet_send (void *user_data, const uint8_t *buffer, int size)
+{
+	ArcnetState *s = user_data ;
+	ArcnetPacket *linux_buffer = (ArcnetPacket*)buffer ;
+	int data_length, length ;
+	ssize_t sent ;
+
+	/* ignore bad packets */
+	if (buffer == NULL || size <= 0 || size != ARCNET_SIZE (buffer)) {
+		return ;
+	}
+
+	/* convert from hardware to linux format (see linux/if_arcnet.h),
+	 * move data to the beginning of the buffer */
+	data_length = ARCNET_DATA_LENGTH (buffer) ;
+	memmove (linux_buffer->soft.raw, ARCNET_DATA (buffer), data_length) ;
+	length = data_length + (linux_buffer->soft.raw - (uint8_t*)linux_buffer) ;
+
+	if (DEBUG_RXTX) dprintf ("send %d bytes\n", length) ;
+	if (DEBUG_DUMP) hex_dump (stderr, buffer, length) ;
+
+	/* send raw packet to the predefined interface */
+	sent = sendto (s->socket, buffer, length, 0, &s->address, sizeof (s->address)) ;
+	if (sent != length) {
+		dperror () ;
+		return ;
+	}
+}
+
+/* cannot receive packet if the device is not ready to receive */
+static int arcnet_can_receive (void *user_data)
+{
+	ArcnetState *s = user_data ;
+	return qemu_can_send_packet (s->vlan_client) ;
+}
+
+/* receive a packet from the host to the emulator vlan */
+static void arcnet_receive (void *user_data)
+{
+	ArcnetState *s = user_data ;
+	uint8_t buffer[ARCNET_MAX_SIZE] ;
+	ArcnetPacket *linux_buffer = (ArcnetPacket*)buffer ;
+	ssize_t received ;
+	int data_length, size ;
+
+	/* receive raw packet from the predefined interface */
+	received = recv (s->socket, buffer, ARCNET_MAX_SIZE, 0) ;
+	if (received <= 0) {
+		dperror () ;
+		return ;
+	}
+
+	if (DEBUG_RXTX) dprintf ("receive %d bytes\n", received) ;
+	if (DEBUG_DUMP) hex_dump (stderr, buffer, received) ;
+
+	/* get data length */
+	data_length = received - (linux_buffer->soft.raw - (uint8_t*)linux_buffer) ;
+	if (data_length <= ARCNET_SHORT_DATA_MAX_SIZE) {
+		size = ARCNET_SHORT_SIZE ;
+	} else if (data_length < ARCNET_LONG_DATA_MIN_SIZE) { /* should not happen */
+		size = ARCNET_LONG_SIZE ;
+		/* data length between short and long => add padding to make a long packet */
+		data_length = ARCNET_LONG_DATA_MIN_SIZE ;
+	} else if (data_length <= ARCNET_LONG_DATA_MAX_SIZE) {
+		size = ARCNET_LONG_SIZE ;
+	} else { /* message too long */
+		errno = EMSGSIZE ;
+		dperror () ;
+		return ;
+	}
+	/* write data length in the header */
+	buffer[ARCNET_SHORT_START] = 0 ;
+	buffer[ARCNET_LONG_START] = 0 ;
+	buffer[ARCNET_START (size)] = size - data_length ;
+	/* convert from linux to hardware format (see linux/if_arcnet.h),
+	 * move data to the end of the buffer */
+	memmove (ARCNET_DATA (buffer), linux_buffer->soft.raw, data_length) ;
+
+	/* send packet to the vlan */
+	qemu_send_packet (s->vlan_client, buffer, size) ;
+}
+
+int arcnet_init (VLANState *vlan, const char *ifname)
+{
+	int error_code ;
+	ArcnetState *s ;
+
+	dprintf ("initialization (%s in vlan%d)\n", ifname, vlan->id) ;
+
+	/* allocate the object of the layer */
+	s = qemu_mallocz (sizeof(ArcnetState)) ;
+	if (s == NULL) {
+		return -1 ;
+	}
+
+	/* open a raw socket */
+	s->socket = socket (PF_PACKET, SOCK_PACKET, htons (ETH_P_ARCNET)) ;
+	if (s->socket < 0) {
+		dperror () ;
+		return -1 ;
+	}
+	/* store address of the specified interface */
+	s->address.sa_family = ARPHRD_ARCNET ;
+	strcpy (s->address.sa_data, ifname) ;
+
+	/* listen the specified interface and call arcnet_receive() on reception */
+    error_code = bind (s->socket, &s->address, sizeof (s->address)) ;
+	if (error_code != 0) {
+		dperror ("bind on %s: ", ifname) ;
+		return -1 ;
+	}
+	error_code = qemu_set_fd_handler2 (s->socket, arcnet_can_receive, arcnet_receive, NULL, s) ;
+	if (error_code != 0) {
+		return -1 ;
+	}
+
+	/* call arcnet_send() when an emulated device emits on the same vlan */
+	s->vlan_client = qemu_new_vlan_client (vlan, arcnet_send, NULL, s) ;
+	if (s->vlan_client == NULL) {
+		return -1 ;
+	}
+
+	snprintf (s->vlan_client->info_str, sizeof (s->vlan_client->info_str), "arcnet user mode") ;
+	dprintf ("vlan=%d, interface=%s, socket=%d\n", vlan->id, ifname, s->socket) ;
+
+	return 0 ;
+}
--- /dev/null
+++ arcnet.h
@@ -0,0 +1,61 @@
+#ifndef ARCNET_H
+#define ARCNET_H
+
+#include "qemu-common.h"
+
+/* arcnet packet size
+ * The packets can have only 2 sizes: short or long */
+#define ARCNET_SHORT_SIZE 256
+#define ARCNET_LONG_SIZE  512
+#define ARCNET_MAX_SIZE ARCNET_LONG_SIZE
+
+/* arcnet header
+ * The source and destination bytes are node ids (0 for broadcast).
+ * If the short start byte is 0, it is a long packet.
+ * The start byte is the offset of the first data byte. */
+enum arcnet_packet_offsets {
+	ARCNET_SOURCE = 0,
+	ARCNET_DESTINATION,
+	ARCNET_SHORT_START,
+	ARCNET_SHORT_HEADER_SIZE,
+	ARCNET_LONG_START = ARCNET_SHORT_HEADER_SIZE,
+	ARCNET_LONG_HEADER_SIZE
+} ;
+
+/* get the size of the packet */
+#define ARCNET_SIZE(buffer) (((uint8_t*)buffer)[ARCNET_SHORT_START] != 0 \
+	? ARCNET_SHORT_SIZE \
+	: ARCNET_LONG_SIZE \
+)
+/* get the size of the header for a packet size */
+#define ARCNET_HEADER_SIZE(size) (size == ARCNET_SHORT_SIZE \
+	? ARCNET_SHORT_HEADER_SIZE \
+	: ARCNET_LONG_HEADER_SIZE \
+)
+/* get the start byte offset for a packet size */
+#define ARCNET_START(size) (size == ARCNET_SHORT_SIZE \
+	? ARCNET_SHORT_START \
+	: ARCNET_LONG_START \
+)
+
+/* The body of a packet coontains padding and data to the end.
+ * The data sizes ranges are not contiguous. */
+#define ARCNET_SHORT_DATA_MIN_SIZE 1
+#define ARCNET_SHORT_DATA_MAX_SIZE (ARCNET_SHORT_SIZE - ARCNET_SHORT_HEADER_SIZE)
+#define ARCNET_LONG_DATA_MIN_SIZE  (ARCNET_LONG_SIZE  - 0xff)
+#define ARCNET_LONG_DATA_MAX_SIZE  (ARCNET_LONG_SIZE  - ARCNET_LONG_HEADER_SIZE)
+/* get the data pointer of a packet */
+#define ARCNET_DATA(buffer) (ARCNET_SIZE(buffer) == ARCNET_SHORT_SIZE \
+	? ((uint8_t*)buffer) + ((uint8_t*)buffer)[ARCNET_SHORT_START] \
+	: ((uint8_t*)buffer) + ((uint8_t*)buffer)[ARCNET_LONG_START ] \
+)
+/* get the data length of a packet */
+#define ARCNET_DATA_LENGTH(buffer) (ARCNET_SIZE(buffer) == ARCNET_SHORT_SIZE \
+	? ARCNET_SHORT_SIZE - ((uint8_t*)buffer)[ARCNET_SHORT_START] \
+	: ARCNET_LONG_SIZE  - ((uint8_t*)buffer)[ARCNET_LONG_START ] \
+)
+
+/* The arcnet layer is initialized for a vlan linked with a real arcnet interface called ifname. */
+int arcnet_init (VLANState *vlan, const char *ifname) ;
+
+#endif /* ARCNET_H */
--- configure
+++ configure
@@ -84,6 +84,7 @@ mingw32="no"
 EXESUF=""
 gdbstub="yes"
 slirp="yes"
+arcnet="no"
 adlib="no"
 oss="no"
 dsound="no"
@@ -188,6 +189,7 @@ SunOS)
 oss="yes"
 linux="yes"
 linux_user="yes"
+arcnet="yes"
 if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
     kqemu="yes"
 fi
@@ -274,6 +276,8 @@ for opt do
   ;;
   --disable-slirp) slirp="no"
   ;;
+  --disable-arcnet) arcnet="no"
+  ;;
   --enable-adlib) adlib="yes"
   ;;
   --disable-kqemu) kqemu="no"
@@ -885,6 +889,10 @@ if test "$slirp" = "yes" ; then
   echo "CONFIG_SLIRP=yes" >> $config_mak
   echo "#define CONFIG_SLIRP 1" >> $config_h
 fi
+if test "$arcnet" = "yes" ; then
+  echo "CONFIG_ARCNET=yes" >> $config_mak
+  echo "#define CONFIG_ARCNET 1" >> $config_h
+fi
 if test "$adlib" = "yes" ; then
   echo "CONFIG_ADLIB=yes" >> $config_mak
   echo "#define CONFIG_ADLIB 1" >> $config_h
--- net.h
+++ net.h
@@ -1,6 +1,8 @@
 #ifndef QEMU_NET_H
 #define QEMU_NET_H
 
+#include "cpu.h"
+
 /* VLANs support */
 
 typedef struct VLANClientState VLANClientState;
@@ -40,6 +42,9 @@ void do_info_network(void);
 
 struct NICInfo {
     uint8_t macaddr[6];
+	uint16_t io;
+	target_phys_addr_t shmem;
+	uint8_t irq;
     const char *model;
     VLANState *vlan;
 };
@@ -47,4 +52,7 @@ struct NICInfo {
 extern int nb_nics;
 extern NICInfo nd_table[MAX_NICS];
 
+/* tools */
+void hex_dump(FILE *f, const uint8_t *buf, int size);
+
 #endif
--- vl.c
+++ vl.c
@@ -106,6 +106,10 @@ int inet_aton(const char *cp, struct in_
 #include "libslirp.h"
 #endif
 
+#if defined(CONFIG_ARCNET)
+#include "arcnet.h"
+#endif
+
 #ifdef _WIN32
 #include <malloc.h>
 #include <sys/timeb.h>
@@ -3433,7 +3437,7 @@ void qemu_chr_close(CharDriverState *chr
 /* network device redirectors */
 
 __attribute__ (( unused ))
-static void hex_dump(FILE *f, const uint8_t *buf, int size)
+void hex_dump(FILE *f, const uint8_t *buf, int size)
 {
     int len, i, j, c;
 
@@ -3491,6 +3495,18 @@ static int parse_macaddr(uint8_t *macadd
     return -1;
 }
 
+static int parse_node_id(uint8_t *node_id, const char *p)
+{
+    long int value;
+	char *last_char;
+    value = strtol(p, &last_char, 0);
+    if (last_char != p && value >= 0 && value < 256) {
+        *node_id = (uint8_t)value;
+        return 0;
+    }
+    return -1;
+}
+
 static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
 {
     const char *p, *p1;
@@ -4690,6 +4706,7 @@ static int net_client_init(const char *s
     }
     if (!strcmp(device, "nic")) {
         NICInfo *nd;
+		int arcnet = 0;
         uint8_t *macaddr;
 
         if (nb_nics >= MAX_NICS) {
@@ -4697,23 +4714,44 @@ static int net_client_init(const char *s
             return -1;
         }
         nd = &nd_table[nb_nics];
+
+        if (get_param_value(buf, sizeof(buf), "model", p)) {
+            nd->model = strdup(buf);
+			if (strcmp(buf, "com90c65") == 0)
+				arcnet = 1;
+        }
         macaddr = nd->macaddr;
         macaddr[0] = 0x52;
         macaddr[1] = 0x54;
         macaddr[2] = 0x00;
         macaddr[3] = 0x12;
         macaddr[4] = 0x34;
-        macaddr[5] = 0x56 + nb_nics;
-
+        macaddr[5] = 0x56 + 0x24 * arcnet + nb_nics;
         if (get_param_value(buf, sizeof(buf), "macaddr", p)) {
             if (parse_macaddr(macaddr, buf) < 0) {
                 fprintf(stderr, "invalid syntax for ethernet address\n");
                 return -1;
             }
         }
-        if (get_param_value(buf, sizeof(buf), "model", p)) {
-            nd->model = strdup(buf);
+        if (get_param_value(buf, sizeof(buf), "node-id", p)) {
+            if (parse_node_id(&macaddr[5], buf) < 0) {
+                fprintf(stderr, "invalid syntax for arcnet id\n");
+                return -1;
+            }
         }
+        if (get_param_value(buf, sizeof(buf), "io", p))
+            nd->io = strtoul(buf, NULL, 0);
+        else
+			nd->io = 0 ;
+        if (get_param_value(buf, sizeof(buf), "irq", p))
+            nd->irq = strtoul(buf, NULL, 0);
+        else
+			nd->irq = 0 ;
+        if (get_param_value(buf, sizeof(buf), "shmem", p))
+            nd->shmem = strtoul(buf, NULL, 0);
+        else
+			nd->shmem = 0 ;
+
         nd->vlan = vlan;
         nb_nics++;
         vlan->nb_guest_devs++;
@@ -4733,6 +4771,15 @@ static int net_client_init(const char *s
         ret = net_slirp_init(vlan);
     } else
 #endif
+#ifdef CONFIG_ARCNET
+    if (!strcmp(device, "user-arcnet")) {
+        if (!get_param_value(buf, sizeof(buf), "ifname", p)) {
+            pstrcpy(buf, sizeof(buf), "arc0");
+        }
+        vlan->nb_host_devs++;
+        ret = arcnet_init(vlan, buf);
+    } else
+#endif
 #ifdef _WIN32
     if (!strcmp(device, "tap")) {
         char ifname[64];
@@ -7544,13 +7591,19 @@ static void help(int exitcode)
            "-name string    set the name of the guest\n"
            "\n"
            "Network options:\n"
-           "-net nic[,vlan=n][,macaddr=addr][,model=type]\n"
+           "-net nic[,vlan=n][,macaddr=addr][,node-id=id][,model=type][,io=x][,irq=y][,shmem=z]\n"
            "                create a new Network Interface Card and connect it to VLAN 'n'\n"
+           "                macaddr is specific to ethernet devices\n"
+           "                node-id is specific to arcnet devices\n"
 #ifdef CONFIG_SLIRP
            "-net user[,vlan=n][,hostname=host]\n"
            "                connect the user mode network stack to VLAN 'n' and send\n"
            "                hostname 'host' to DHCP clients\n"
 #endif
+#ifdef CONFIG_ARCNET
+           "-net user-arcnet[,vlan=n][,ifname=name]\n"
+           "                connect the user mode arcnet stack to VLAN 'n'\n"
+#endif
 #ifdef _WIN32
            "-net tap[,vlan=n],ifname=name\n"
            "                connect the host TAP network interface to VLAN 'n'\n"
--- /dev/null
+++ hw/com90c65.c
@@ -0,0 +1,725 @@
+/* This is a hardware NIC (Network Interface Card)
+ * emulation of the COM90C66 device family.
+ *
+ * It works on an (emulated) ISA bus.
+ * Access can be 8 or 16 bits, in I/O or memory mapped mode.
+ * There is a burst mode called command chaining (not implemented).
+ * The datasheet is available at:
+ * http://www.smsc.com/main/tools/discontinued/90c66.pdf
+ *
+ * The device communicates with its vlan.
+ * An arcnet layer must be connected to the same vlan.
+ * A send comes from the driver to the vlan.
+ * A receive comes from the vlan to the driver.
+ */
+
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+#include "net.h"
+#include "arcnet.h"
+
+/* name of the device (debug usage) */
+#define DEVICE "com90c65"
+/* version of the saved state file */
+#define VERSION 0
+
+#ifdef DEBUG
+	/* set it to 1 to print configuration changes */
+	#define DEBUG_CONFIG 1
+	/* set it to 1 to print send/receive events */
+	#define DEBUG_RXTX   1
+	/* set it to 1 to print sent/received packets */
+	#define DEBUG_DUMP   0
+	/* set it to 1 to print interrupt related events */
+	#define DEBUG_IRQ    0
+	/* set it to 1 to print read/write of registers */
+	#define DEBUG_IO_RW  0
+	/* set it to 1 to print read/write of memory mapped */
+	#define DEBUG_MM_RW  0
+#else
+	#define DEBUG_CONFIG 0
+	#define DEBUG_RXTX   0
+	#define DEBUG_DUMP   0
+	#define DEBUG_IRQ    0
+	#define DEBUG_IO_RW  0
+	#define DEBUG_MM_RW  0
+#endif
+
+#define eprintf(fmt, args...) fprintf (stderr, DEVICE ": " fmt, ## args)
+#define not_implemented0(fmt, args...) eprintf (fmt ": not implemented\n", ## args)
+#ifdef DEBUG
+	#define dprintf0(fmt, args...) fprintf (stderr, fmt, ## args)
+	#define not_implemented(fmt, args...) not_implemented0 (fmt, ## args)
+#else
+	#define dprintf0(fmt, args...) do {} while (0)
+	#define not_implemented(fmt, args...) { \
+		static int once = 0 ; \
+		if (! once) { once = 1 ; not_implemented0 (fmt, ## args) ; } \
+	}
+#endif
+#define dprintf(fmt, args...) dprintf0 (DEVICE ": " fmt, ## args)
+#define BOOL_TEXT(state) (state ? "enable" : "disable")
+
+/* default I/O addresses */
+#define IO_BASES_MAX 8
+static int io_bases[IO_BASES_MAX] = {0x260, 0x290, 0x2e0, 0x2f0, 0x300, 0x350, 0x380, 0x3e0} ;
+
+/* known byte to check the device at initialization */
+#define RESET_MAGIC_NUMBER 0xD1
+
+/* the 2 kB internal memory is partitioned in 4 blocks */
+#define PAGE_SIZE ARCNET_MAX_SIZE
+#define MEM_SIZE (PAGE_SIZE * 4)
+
+/* addresses can be read/write or can have different uses in read and write */
+enum register_offsets {
+	OFFSET_STATUS       = 0x0, /* R   */
+	OFFSET_IRQ_MASK     = 0x0, /*   W */
+	OFFSET_DIAG_STATUS  = 0x1, /* R   */
+	OFFSET_COMMAND      = 0x1, /*   W */
+	OFFSET_CONFIG       = 0x2, /* R/W */
+	OFFSET_IO_SELECT    = 0x3, /* R   */
+	OFFSET_MEM_SELECT   = 0x4, /* R   */
+	OFFSET_NODE_ID      = 0x5, /* R/W */
+	OFFSET_EXTERNAL     = 0x7, /*   W */
+	OFFSET_RESET_BEGIN  = 0x8, /* R/W */
+	/*     RESET          0x9     R/W */
+	/*     RESET          0xA     R/W */
+	OFFSET_RESET_END    = 0xB, /* R/W */
+	OFFSET_DATA_LOW     = 0xC, /* R/W */
+	OFFSET_DATA_HIGH    = 0xD, /* R/W */
+	OFFSET_ADDR_LOW     = 0xE, /* R/W */
+	OFFSET_ADDR_HIGH    = 0xF, /* R/W */
+	REGISTERS_SIZE
+} ;
+
+enum status_bits {
+	BIT_TA    = 1 << 0,
+	BIT_TMA   = 1 << 1,
+	BIT_RECON = 1 << 2,
+	BIT_TEST  = 1 << 3,
+	BIT_POR   = 1 << 4,
+	BIT_RI    = 1 << 7
+} ;
+/* RECON should not be set by default (see status register in datasheet) but is needed by Linux */
+#define DEFAULT_STATUS (BIT_POR | BIT_RECON | BIT_RI | BIT_TA)
+/* these status bits can be "masked" to generate an IRQ */
+#define IRQ_MASK (BIT_TA | BIT_RECON | BIT_RI)
+
+enum diag_status_bits {
+	BIT_TOKEN   = 1 << 4,
+	BIT_RCVACT  = 1 << 5,
+	BIT_MYRECON = 1 << 7
+} ;
+
+/* commands can be distinguished by their first 3 bits */
+#define COMMAND_MASK 7
+enum commands {
+	COMMAND_CLEAR_IRQ    = 0,
+	COMMMAND_DISABLE_TX  = 1,
+	COMMMAND_DISABLE_RX  = 2,
+	COMMMAND_ENABLE_TX   = 3,
+	COMMMAND_ENABLE_RX   = 4,
+	COMMMAND_CONFIG      = 5,
+	COMMMAND_CLEAR_FLAGS = 6
+} ;
+enum command_bits {
+	BIT_CLEAR_IRQ      = 1 << 3,
+	BIT_RX_BROADCAST   = 1 << 7,
+	BIT_TX_LONG_PACKET = 1 << 3,
+	BIT_CLEAR_POR      = 1 << 3,
+	BIT_CLEAR_RECON    = 1 << 4
+} ;
+/* bits 3-4 of rx/tx commands define a page number */
+#define PAGE(command) ((command & 0x18) >> 3)
+
+enum config_bits {
+	BIT_TXOFF     = 1 << 0,
+	BIT_IO_ACCESS = 1 << 1,
+	BIT_WAIT      = 1 << 2,
+	BIT_ET2       = 1 << 3,
+	BIT_ET1       = 1 << 4,
+	BIT_DECODE    = 1 << 5,
+	BIT_CCHEN     = 1 << 6,
+	BIT_16EN      = 1 << 7
+} ;
+#define DEFAULT_CONFIG (BIT_ET1 | BIT_ET2 | BIT_WAIT)
+
+/* addresses are 11 bits wide to access to the 2 kB of the internal memory */
+#define ADDRESS_MASK 0x7ff
+enum address_bits {
+	BIT_AUTO_INC = 1 << 14
+} ;
+
+/* THE object */
+typedef struct COM90C65State {
+
+	/* internal storage */
+	target_phys_addr_t mm_base ;
+	uint8_t mem[MEM_SIZE] ;
+	uint8_t long_packet_enabled ;
+	uint8_t broadcast_enabled ;
+	uint8_t reception_page ;
+
+	/* registers */
+	uint8_t status ;
+	uint8_t irq_mask ;
+	uint8_t diag_status ;
+	uint8_t config ;
+	uint8_t io_select ;
+	uint8_t mem_select ;
+	uint8_t node_id ;
+	uint16_t data ;
+	uint16_t address ;
+
+	/* registers access by arrays */
+	struct {
+		uint8_t *read[REGISTERS_SIZE] ;
+		uint8_t *write[REGISTERS_SIZE] ;
+	} registers ;
+
+	/* qemu handlers */
+	qemu_irq irq ;
+	VLANClientState *vlan_client ;
+
+} COM90C65State ;
+
+/* map the register variables with 2 arrays for read and write access */
+static void com90c65_registers_access_init (COM90C65State *s) {
+	/* FIXME: what happens if WORDS_BIGENDIAN != TARGET_WORDS_BIGENDIAN ? */
+	#if __BYTE_ORDER == __LITTLE_ENDIAN
+		#define LOW_BYTE_OFFSET  0
+		#define HIGH_BYTE_OFFSET 1
+	#elif __BYTE_ORDER == __BIG_ENDIAN
+		#define LOW_BYTE_OFFSET  1
+		#define HIGH_BYTE_OFFSET 0
+	#endif
+
+	/* dummy is used for registers whose written value is not used */
+	static uint8_t dummy ;
+	/* structure is initialized with 0, so the unreferenced registers are NULL */
+	s->registers.read [OFFSET_STATUS]      =           &s->status ;
+	s->registers.write[OFFSET_IRQ_MASK]    =           &s->irq_mask ;
+	s->registers.read [OFFSET_DIAG_STATUS] =           &s->diag_status ;
+	s->registers.write[OFFSET_COMMAND]     =           &dummy ;
+	s->registers.read [OFFSET_CONFIG]      =           &s->config ;
+	s->registers.write[OFFSET_CONFIG]      =           &s->config ;
+	s->registers.read [OFFSET_IO_SELECT]   =           &s->io_select ;
+	s->registers.read [OFFSET_MEM_SELECT]  =           &s->mem_select ;
+	s->registers.read [OFFSET_NODE_ID]     =           &s->node_id ;
+	s->registers.write[OFFSET_NODE_ID]     =           &s->node_id ;
+	s->registers.write[OFFSET_EXTERNAL]    =           &dummy ;
+	s->registers.read [OFFSET_DATA_LOW]    = (uint8_t*)&s->data + LOW_BYTE_OFFSET ;
+	s->registers.write[OFFSET_DATA_LOW]    = (uint8_t*)&s->data + LOW_BYTE_OFFSET ;
+	s->registers.read [OFFSET_DATA_HIGH]   = (uint8_t*)&s->data + HIGH_BYTE_OFFSET ;
+	s->registers.write[OFFSET_DATA_HIGH]   = (uint8_t*)&s->data + HIGH_BYTE_OFFSET ;
+	s->registers.read [OFFSET_ADDR_LOW]    = (uint8_t*)&s->address + LOW_BYTE_OFFSET ;
+	s->registers.write[OFFSET_ADDR_LOW]    = (uint8_t*)&s->address + LOW_BYTE_OFFSET ;
+	s->registers.read [OFFSET_ADDR_HIGH]   = (uint8_t*)&s->address + HIGH_BYTE_OFFSET ;
+	s->registers.write[OFFSET_ADDR_HIGH]   = (uint8_t*)&s->address + HIGH_BYTE_OFFSET ;
+}
+
+/* save current state of the device in a file */
+static void com90c65_save (QEMUFile *file, void *user_data) {
+	COM90C65State *s = user_data ;
+
+	dprintf ("save VM\n") ;
+	qemu_put_8s (file, &s->status) ;
+	qemu_put_8s (file, &s->irq_mask) ;
+	qemu_put_8s (file, &s->diag_status) ;
+	qemu_put_8s (file, &s->config) ;
+	qemu_put_8s (file, &s->io_select) ;
+	qemu_put_8s (file, &s->mem_select) ;
+	qemu_put_8s (file, &s->node_id) ;
+	qemu_put_be16s (file, &s->data) ;
+	qemu_put_be16s (file, &s->address) ;
+	qemu_put_buffer (file, s->mem, MEM_SIZE) ;
+	qemu_put_8s (file, &s->long_packet_enabled) ;
+	qemu_put_8s (file, &s->broadcast_enabled) ;
+	qemu_put_8s (file, &s->reception_page) ;
+}
+
+/* load the device state from a file in a backward compatible way */
+static int com90c65_load (QEMUFile *file, void *user_data, int version) {
+	COM90C65State *s = user_data ;
+
+	if (version > VERSION)
+		return -EINVAL ;
+
+	dprintf ("load VM\n") ;
+	qemu_get_8s (file, &s->status) ;
+	qemu_get_8s (file, &s->irq_mask) ;
+	qemu_get_8s (file, &s->diag_status) ;
+	qemu_get_8s (file, &s->config) ;
+	qemu_get_8s (file, &s->io_select) ;
+	qemu_get_8s (file, &s->mem_select) ;
+	qemu_get_8s (file, &s->node_id) ;
+	qemu_get_be16s (file, &s->data) ;
+	qemu_get_be16s (file, &s->address) ;
+	qemu_get_buffer (file, s->mem, MEM_SIZE) ;
+	qemu_get_8s (file, &s->long_packet_enabled) ;
+	qemu_get_8s (file, &s->broadcast_enabled) ;
+	qemu_get_8s (file, &s->reception_page) ;
+
+	return 0 ;
+}
+
+/* print device configuration (for debug use) of bits set in mask */
+static void com90c65_print_config (uint8_t config, uint8_t mask) {
+	if (DEBUG_CONFIG) {
+		if (mask & BIT_TXOFF)
+			dprintf ("%s tx\n", BOOL_TEXT (~ config & BIT_TXOFF)) ;
+		if (mask & BIT_IO_ACCESS)
+			dprintf ("%s buffer access\n", config & BIT_IO_ACCESS ? "I/O" : "memory") ;
+		if (mask & BIT_WAIT)
+			dprintf ("%s zero wait state\n", BOOL_TEXT (~ config & BIT_WAIT)) ;
+		if (mask & (BIT_ET1 | BIT_ET2))
+			dprintf ("extended timeout = %d%d\n", (config & BIT_ET1) / BIT_ET1, (config & BIT_ET2) / BIT_ET2) ;
+		if (mask & (BIT_DECODE | BIT_IO_ACCESS))
+			dprintf ("%dK ROM\n", config & BIT_IO_ACCESS ? (config & BIT_DECODE ? 16 : 8) : (config & BIT_DECODE ? 2 : 128)) ;
+		if (mask & BIT_CCHEN)
+			dprintf ("%s command chaining\n", BOOL_TEXT (config & BIT_CCHEN)) ;
+		if (mask & BIT_16EN)
+			dprintf ("%d-bit\n", config & BIT_16EN ? 16 : 8) ;
+	}
+	if (config & mask & BIT_CCHEN)
+		not_implemented ("command chaining") ;
+}
+
+/* convert I/O address to an index value in the dedicated register */
+static int com90c65_set_io_select (COM90C65State *s, int io_base) {
+	int index ;
+	for (index = 0 ; index < IO_BASES_MAX ; index ++) {
+		if (io_base == io_bases[index]) {
+			s->io_select = index ;
+			return 0 ;
+		}
+	}
+	return -1 ;
+}
+
+/* generate an IRQ if a masked status bit becomes 1 */
+static void com90c65_update_irq (COM90C65State *s, int generate) {
+	static uint8_t old_isr = 0 ;
+	uint8_t isr = s->status & s->irq_mask & IRQ_MASK ;
+	int trigger = (isr & ~old_isr) != 0 ;
+	old_isr = isr ;
+	if (generate) {
+		if (DEBUG_IRQ) {
+			dprintf ("status=0x%x", s->status) ;
+			if (trigger)
+				dprintf0 (" => IRQ\n") ;
+			else
+				dprintf0 ("\n") ;
+		}
+		qemu_set_irq (s->irq, trigger) ;
+	}
+}
+
+/* reset the device with the default values,
+ * stands for hard and soft reset */
+static void com90c65_reset (void *user_data) {
+	COM90C65State *s = user_data ;
+
+	dprintf ("reset\n") ;
+
+	s->status = DEFAULT_STATUS ;
+	s->irq_mask = 0 ;
+	s->diag_status = 0 ;
+	s->config = DEFAULT_CONFIG ;
+
+	/* an arbitrary number is set and can be check for a valid reset */
+	s->mem[0] = RESET_MAGIC_NUMBER ;
+	/* the node id can be read after a reset */
+	s->mem[1] = s->node_id ;
+
+	s->long_packet_enabled = 0 ;
+	s->broadcast_enabled = 0 ;
+
+	com90c65_update_irq (s, 0) ;
+}
+
+static uint32_t com90c65_reset_on_read (void *user_data, uint32_t address) {
+	com90c65_reset (user_data) ;
+	return 0 ;
+}
+
+static void com90c65_reset_on_write (void *user_data, uint32_t address, uint32_t value) {
+	com90c65_reset (user_data) ;
+}
+
+/* send the packet of the specified memory page to the vlan */
+static void com90c65_send (COM90C65State *s, int page) {
+	const uint8_t *buffer = &s->mem[PAGE_SIZE * page] ;
+	int size ;
+
+	if (s->long_packet_enabled)
+		size = ARCNET_SIZE (buffer) ;
+	else
+		size = ARCNET_SHORT_SIZE ;
+
+	if (DEBUG_RXTX) dprintf ("send %d bytes\n", size) ;
+	if (DEBUG_DUMP) hex_dump (stderr, buffer, size) ;
+
+	/* send to other clients of the vlan,
+	 * it should be received by an arcnet layer */
+	qemu_send_packet (s->vlan_client, buffer, size) ;
+}
+
+/* receive a packet from the vlan to the specified memory page */
+static void com90c65_receive (void *user_data, const uint8_t *buffer, int size) {
+	COM90C65State *s = user_data ;
+
+	if (buffer == NULL || size <= 0)
+		return ;
+
+	/* a broadcasted packet has a recipient 0 and can be disabled by a command */
+	if (buffer [ARCNET_DESTINATION] == 0 && ! s->broadcast_enabled) {
+		if (DEBUG_RXTX) dprintf ("ignore broadcasted packet\n") ;
+		return ;
+	}
+
+	if (DEBUG_RXTX) dprintf ("receive %d bytes\n", size) ;
+	if (DEBUG_DUMP) hex_dump (stderr, buffer, size) ;
+
+	/* copy the packet into the internal memory page specified on rx start */
+	memcpy (&s->mem[PAGE_SIZE * (s->reception_page & 3)], buffer, size) ;
+
+	/* an IRQ signals the packet availability */
+	s->status |= BIT_RI ;
+	com90c65_update_irq (s, 1) ;
+}
+
+/* the arcnet layer send a packet to the vlan only when the device is ready to receive */
+static int com90c65_can_receive (void *user_data) {
+	COM90C65State *s = user_data ;
+	return (s->status & BIT_RI) == 0 ;
+}
+
+/* various actions must be done after a write in the command register */
+static void com90c65_command (COM90C65State *s, uint32_t command) {
+	switch (command & COMMAND_MASK) {
+		/* command chaining is not implemented */
+		case COMMAND_CLEAR_IRQ :
+			if ((command & BIT_CLEAR_IRQ) == 0) {
+				if (DEBUG_IRQ) dprintf ("command chaining: clear tx interrupt\n") ;
+				/* TODO (not used in Linux) */
+			} else {
+				if (DEBUG_IRQ) dprintf ("command chaining: clear rx interrupt\n") ;
+				/* TODO (not used in Linux) */
+			}
+			break ;
+		/* the emulation has no pending transmit, so cancel does nothing */
+		case COMMMAND_DISABLE_TX :
+			if (DEBUG_RXTX) dprintf ("cancel tx\n") ;
+			s->status |= BIT_TA ;
+			break ;
+		/* cancel reception if it has not begun */
+		case COMMMAND_DISABLE_RX :
+			if (DEBUG_RXTX) dprintf ("cancel rx\n") ;
+			s->status |= BIT_RI ;
+			/* save the bit state without generate an IRQ */
+			com90c65_update_irq (s, 0) ;
+			break ;
+		/* send the packet of the specified page */
+		case COMMMAND_ENABLE_TX :
+			if (DEBUG_RXTX) dprintf ("start tx from page %d\n", PAGE (command)) ;
+			s->status &= ~ (BIT_TA | BIT_TMA) ;
+			/* save the resetted bits states */
+			com90c65_update_irq (s, 0) ;
+			com90c65_send (s, PAGE (command)) ;
+			if (1) { /* on acknowledgement (assume it is ok) */
+				s->status |= BIT_TMA ;
+			}
+			if (1) { /* on transmit completion (assume it is ok) */
+				s->status |= BIT_TA ;
+				/* generate an IRQ */
+				com90c65_update_irq (s, 1) ;
+			}
+			break ;
+		/* prepare to receive a packet in the specified page */
+		case COMMMAND_ENABLE_RX :
+			s->broadcast_enabled = command & BIT_RX_BROADCAST ;
+			s->reception_page = PAGE (command) ;
+			if (DEBUG_RXTX) dprintf ("start rx to page %d (broadcast %sd)\n", s->reception_page, BOOL_TEXT (s->broadcast_enabled)) ;
+			s->status &= ~ BIT_RI ;
+			/* save the cleared bit state (IRQ is generated on reception) */
+			com90c65_update_irq (s, 0) ;
+			break ;
+		/* set max size of packet to send */
+		case COMMMAND_CONFIG :
+			s->long_packet_enabled = command & BIT_TX_LONG_PACKET ;
+			if (DEBUG_CONFIG) dprintf ("%s long packet\n", BOOL_TEXT (s->long_packet_enabled)) ;
+			break ;
+		/* clear status bits */
+		case COMMMAND_CLEAR_FLAGS :
+			if (command & BIT_CLEAR_POR) {
+				if (DEBUG_IO_RW) dprintf ("clear flag POR\n") ;
+				s->status &= ~ BIT_POR ;
+			}
+			if (command & BIT_CLEAR_RECON) {
+				if (DEBUG_IO_RW) dprintf ("clear flag RECON\n") ;
+				s->status &= ~ BIT_RECON ;
+				/* save the cleared bit state */
+				com90c65_update_irq (s, 0) ;
+			}
+			break ;
+		default :
+			dprintf ("an invalid command (0x%x) is received\n", command) ;
+	}
+}
+
+/* read data in internal memory at the address written in the address registers
+ * and set it in the data registers */
+static void com90c65_update_data (COM90C65State *s) {
+	s->data = s->mem[s->address & ADDRESS_MASK] | s->mem[(s->address & ADDRESS_MASK) + 1] << 8 ;
+}
+
+/* increment address registers if needed and update data registers,
+ * auto_increment can be 1 or 2 for 8 or 16 bits access */
+static void com90c65_auto_increment (COM90C65State *s, int auto_increment) {
+	if (s->address & BIT_AUTO_INC)
+		s->address = ((s->address & ADDRESS_MASK) + auto_increment) | BIT_AUTO_INC ;
+	com90c65_update_data (s) ;
+}
+
+/* return the value of the register */
+static uint32_t com90c65_ioport_read (COM90C65State *s, uint32_t address, int width) {
+	uint8_t *reg ;
+	uint32_t value ;
+	int auto_increment = 1 ;
+
+	address &= 0xf ;
+
+	if (width == 16 && s->config & BIT_16EN && address == OFFSET_DATA_LOW) {
+		/* 16 bits: read the word of the data register */
+		value = s->data ;
+		auto_increment = 2 ;
+	} else {
+		/* 8 bits: read the byte of the specified register */
+		reg = s->registers.read[address] ;
+		if (reg == NULL) {
+			dprintf ("an invalid register (0x%X) is read\n", address) ;
+			return 0xaa ;
+		}
+		value = *reg ;
+	}
+	if (DEBUG_IO_RW) dprintf ("register 0x%X is read (0x%x)\n", address, value) ;
+
+	/* do specific actions */
+	switch (address) {
+		case OFFSET_DIAG_STATUS :
+			/* reset diag_status after a read */
+			s->diag_status = 0 ;
+			break ;
+		case OFFSET_DATA_LOW :
+			/* load next data if the auto increment bit is set */
+			com90c65_auto_increment (s, auto_increment) ;
+			break ;
+	}
+
+	return value ;
+}
+
+/* a register read can be 8-bit wide (inb syscall) */
+static uint32_t com90c65_ioport_read8 (void *user_data, uint32_t address) {
+	COM90C65State *s = user_data ;
+	return com90c65_ioport_read (s, address, 8) ;
+}
+
+/* a register read can be 16-bit wide (inw syscall) */
+static uint32_t com90c65_ioport_read16 (void *user_data, uint32_t address) {
+	COM90C65State *s = user_data ;
+	return com90c65_ioport_read (s, address, 16) ;
+}
+
+/* write value into the specified register */
+static void com90c65_ioport_write (COM90C65State *s, uint32_t address, uint32_t value, int width) {
+	uint8_t *reg ;
+	uint8_t old_value = 0 ;
+	int auto_increment = 1 ;
+
+	address &= 0xf ;
+
+	if (width == 16 && s->config & BIT_16EN && address == OFFSET_DATA_LOW) {
+		/* 16 bits: write the word into the data register */
+		value &= 0xffff ;
+		s->data = value ;
+		auto_increment = 2 ;
+	} else {
+		/* 8 bit: write the byte into the specified register */
+		value &= 0xff ;
+		reg = s->registers.write[address] ;
+		if (reg == NULL) {
+			dprintf ("an invalid register (0x%X) is written\n", address) ;
+			return ;
+		}
+		old_value = *reg ;
+		*reg = value ;
+	}
+	if (DEBUG_IO_RW) dprintf ("register 0x%X is written (0x%x)\n", address, value) ;
+
+	/* do specific actions */
+	switch (address) {
+		case OFFSET_IRQ_MASK :
+			if (DEBUG_IRQ) dprintf ("mask=0x%x\n", s->irq_mask) ;
+			/* generate an IRQ if it unmaks a bit set at 1 */
+			com90c65_update_irq (s, 1) ;
+			break ;
+		case OFFSET_COMMAND :
+			com90c65_command (s, value) ;
+			break ;
+		case OFFSET_CONFIG :
+			/* print the configuration changes (debug only) */
+			com90c65_print_config (s->config, s->config ^ old_value) ;
+			break ;
+		case OFFSET_DATA_LOW :
+			/* write the data in the internal memory */
+			s->mem[s->address & ADDRESS_MASK] = s->data & 0xff ;
+			if (auto_increment == 2) {
+				/* write the upper byte of the 16-bit data */
+				s->mem[(s->address + 1) & ADDRESS_MASK] = s->data >> 8 ;
+			}
+			/* increment address for the next data if the auto increment bit is set */
+			com90c65_auto_increment (s, auto_increment) ;
+			break ;
+		case OFFSET_ADDR_LOW :
+		case OFFSET_ADDR_HIGH :
+			/* load data of the specified address for a read */
+			com90c65_update_data (s) ;
+			break ;
+	}
+}
+
+/* a register write can be 8-bit wide (outb syscall) */
+static void com90c65_ioport_write8 (void *user_data, uint32_t address, uint32_t value) {
+	COM90C65State *s = user_data ;
+	com90c65_ioport_write (s, address, value, 8) ;
+}
+
+/* a register write can be 16-bit wide (outw syscall) */
+static void com90c65_ioport_write16 (void *user_data, uint32_t address, uint32_t value) {
+	COM90C65State *s = user_data ;
+	com90c65_ioport_write (s, address, value, 16) ;
+}
+
+/* read from memory mapped */
+static uint32_t com90c65_mm_read (void *user_data, target_phys_addr_t address, int size) {
+	COM90C65State *s = user_data ;
+	uint32_t value = 0 ;
+	int offset ;
+	for (offset = 0 ; offset < size ; offset ++)
+		value |= s->mem[address - s->mm_base + offset] << (offset << 3) ;
+	if (DEBUG_MM_RW) dprintf ("read %d bytes from memory 0x%x (0x%x)\n", size, address, value) ;
+	return value ;
+}
+
+static uint32_t com90c65_mm_read8 (void *user_data, target_phys_addr_t address) {
+	return com90c65_mm_read (user_data, address, 1) ;
+}
+
+static uint32_t com90c65_mm_read16 (void *user_data, target_phys_addr_t address) {
+	return com90c65_mm_read (user_data, address, 2) ;
+}
+
+static uint32_t com90c65_mm_read32 (void *user_data, target_phys_addr_t address) {
+	return com90c65_mm_read (user_data, address, 4) ;
+}
+
+/* memory read can be 8, 16 or 32 bits wide */
+static CPUReadMemoryFunc *com90c65_mm_reads[] = {
+	&com90c65_mm_read8,
+	&com90c65_mm_read16,
+	&com90c65_mm_read32,
+} ;
+
+/* write to memory mapped */
+static void com90c65_mm_write (void *user_data, target_phys_addr_t address, uint32_t value, int size) {
+	COM90C65State *s = user_data ;
+	int offset ;
+	if (DEBUG_MM_RW) dprintf ("write %d bytes to memory: 0x%x (0x%x)\n", size, address, value) ;
+	for (offset = 0 ; offset < size ; offset ++)
+		s->mem[address - s->mm_base + offset] = value >> (offset << 3) & 0xff ;
+}
+
+static void com90c65_mm_write8 (void *user_data, target_phys_addr_t address, uint32_t value) {
+	com90c65_mm_write (user_data, address, value, 1) ;
+}
+
+static void com90c65_mm_write16 (void *user_data, target_phys_addr_t address, uint32_t value) {
+	com90c65_mm_write (user_data, address, value, 2) ;
+}
+
+static void com90c65_mm_write32 (void *user_data, target_phys_addr_t address, uint32_t value) {
+	com90c65_mm_write (user_data, address, value, 4) ;
+}
+
+/* memory write can be 8, 16 or 32 bits wide */
+static CPUWriteMemoryFunc *com90c65_mm_writes[] = {
+	&com90c65_mm_write8,
+	&com90c65_mm_write16,
+	&com90c65_mm_write32,
+} ;
+
+/* main function: initialization,
+ * command line parameters are stored in nic_info */
+void isa_com90c65_init (NICInfo *nic_info, qemu_irq irq) {
+	COM90C65State *s ;
+	int io_memory ;
+	int error_code ;
+
+	dprintf ("initialization (" DEVICE " in vlan%d)\n", nic_info->vlan->id) ;
+
+	/* instantiate the object and initialize it to 0 */
+	s = qemu_mallocz (sizeof(COM90C65State)) ;
+	if (s == NULL)
+		return ;
+
+	/* initialize IRQ and registers */
+	s->irq = irq ;
+	error_code = com90c65_set_io_select (s, nic_info->io) ;
+	if (error_code != 0) {
+		eprintf ("bad I/O address: %X\n", nic_info->io) ;
+		return ;
+	}
+	s->mem_select = 0 ; /* not used */
+	s->node_id = nic_info->macaddr[5] ;
+	com90c65_reset (s) ;
+
+	if (nic_info->vlan != NULL) {
+		/* register function which receive packets from the vlan (sent by the arcnet bottom layer) */
+		s->vlan_client = qemu_new_vlan_client (nic_info->vlan, com90c65_receive, com90c65_can_receive, s) ;
+		if (s->vlan_client == NULL)
+			return ;
+		snprintf (s->vlan_client->info_str, sizeof (s->vlan_client->info_str), DEVICE " node_id=%02x", s->node_id) ;
+		dprintf ("vlan=%d, " DEVICE " io_id=%d\n", nic_info->vlan->id, s->io_select) ;
+		dprintf ("device info: %s, io=0x%03x, shmem=0x%05x, irq=%d\n", s->vlan_client->info_str, nic_info->io, nic_info->shmem, nic_info->irq) ;
+	}
+
+	/* initialize read/write accesses to the registers for easy implementation of the read/write functions */
+	com90c65_registers_access_init (s) ;
+	/* register 8-bit accesses for all the range of I/O addresses */
+	register_ioport_read  (nic_info->io,      REGISTERS_SIZE, 1, com90c65_ioport_read8,   s) ;
+	register_ioport_write (nic_info->io,      REGISTERS_SIZE, 1, com90c65_ioport_write8,  s) ;
+	/* register 16-bit access for data register wich need a special handling,
+	 * other 16-bits acesses are simulated by successives 8-bit accesses by default */
+	register_ioport_read  (nic_info->io + OFFSET_DATA_LOW, 1, 2, com90c65_ioport_read16,  s) ;
+	register_ioport_write (nic_info->io + OFFSET_DATA_LOW, 1, 2, com90c65_ioport_write16, s) ;
+
+	/* register reset by overwriting a range of I/O addresses which trigger a soft reset */
+	register_ioport_read  (nic_info->io + OFFSET_RESET_BEGIN,
+	               OFFSET_RESET_END - OFFSET_RESET_BEGIN + 1, 1, com90c65_reset_on_read,  s) ;
+	register_ioport_write (nic_info->io + OFFSET_RESET_BEGIN,
+	               OFFSET_RESET_END - OFFSET_RESET_BEGIN + 1, 1, com90c65_reset_on_write, s) ;
+	/* register a hard reset used by qemu */
+	qemu_register_reset (com90c65_reset, s) ;
+
+	/* reserve a physical memory range and map it to the internal memory */
+	s->mm_base = nic_info->shmem ;
+	io_memory = cpu_register_io_memory (0, com90c65_mm_reads, com90c65_mm_writes, s) ;
+	cpu_register_physical_memory (s->mm_base, MEM_SIZE, io_memory) ;
+
+	/* register functions to suspend/resume the current state */
+	register_savevm (DEVICE, nic_info->io, VERSION, com90c65_save, com90c65_load, s) ;
+}
--- hw/pc.c
+++ hw/pc.c
@@ -648,6 +648,11 @@ static const int ide_irq[2] = { 14, 15 }
 static int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 };
 static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
 
+#define COM90C65_NB_MAX 8
+static int com90c65_io[COM90C65_NB_MAX] = { 0x2e0, 0x2f0, 0x300, 0x350, 0x380, 0x3e0, 0x260, 0x290 };
+static int com90c65_irq[COM90C65_NB_MAX] = { 5, 6, 7, 9, 10, 11, 3, 4 };
+static int com90c65_shmem[COM90C65_NB_MAX] = { 0xd0000, 0xd0800, 0xd1000, 0xd1800, 0xd4000, 0xd4800, 0xd5000, 0xd5800 };
+
 static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
 static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
 
@@ -696,6 +701,22 @@ static void pc_init_ne2k_isa(NICInfo *nd
     nb_ne2k++;
 }
 
+static void pc_init_com90c65_isa(NICInfo *nd, qemu_irq *pic)
+{
+    static int nb_com90c65 = 0;
+
+    if (nb_com90c65 == COM90C65_NB_MAX)
+        return;
+	if (nd->io == 0)
+		nd->io = com90c65_io[nb_com90c65];
+	if (nd->irq == 0)
+		nd->irq = com90c65_irq[nb_com90c65];
+	if (nd->shmem == 0)
+		nd->shmem = com90c65_shmem[nb_com90c65];
+    isa_com90c65_init(nd, pic[nd->irq]);
+    nb_com90c65++;
+}
+
 /* PC hardware initialisation */
 static void pc_init1(int ram_size, int vga_ram_size,
                      const char *boot_device, DisplayState *ds,
@@ -920,6 +941,8 @@ static void pc_init1(int ram_size, int v
         }
         if (strcmp(nd->model, "ne2k_isa") == 0) {
             pc_init_ne2k_isa(nd, i8259);
+        } else if (strcmp(nd->model, "com90c65") == 0) {
+            pc_init_com90c65_isa(nd, i8259);
         } else if (pci_enabled) {
             if (strcmp(nd->model, "?") == 0)
                 fprintf(stderr, "qemu: Supported ISA NICs: ne2k_isa\n");
--- hw/pc.h
+++ hw/pc.h
@@ -142,4 +142,8 @@ void pci_piix4_ide_init(PCIBus *bus, Blo
 
 void isa_ne2000_init(int base, qemu_irq irq, NICInfo *nd);
 
+/* com90c65.c */
+
+void isa_com90c65_init(NICInfo *nd, qemu_irq irq);
+
 #endif
