diff --git a/Makefile.objs b/Makefile.objs
index 70c5c79..5fdec97 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -66,6 +66,7 @@ net-obj-y = net.o
 net-nested-y = queue.o checksum.o util.o
 net-nested-y += socket.o
 net-nested-y += dump.o
+net-nested-y += user-arcnet.o
 net-nested-$(CONFIG_POSIX) += tap.o
 net-nested-$(CONFIG_LINUX) += tap-linux.o
 net-nested-$(CONFIG_WIN32) += tap-win32.o
@@ -290,6 +291,8 @@ hw-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
 hw-obj-$(CONFIG_SMC91C111) += smc91c111.o
 hw-obj-$(CONFIG_LAN9118) += lan9118.o
 hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o
+hw-obj-$(CONFIG_COM90C65_ISA) += com90c65.o com90c65-isa.o
+hw-obj-$(CONFIG_DE100_ISA) += de100-isa.o
 hw-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o
 
 # IDE
diff --git a/VERSION b/VERSION
index 9084fa2..64b4f32 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.1.0
+1.1.0-owi
diff --git a/arcnet.h b/arcnet.h
new file mode 100644
index 0000000..173f3f0
--- /dev/null
+++ b/arcnet.h
@@ -0,0 +1,62 @@
+/*
+ * QEMU ARCnet generic declarations
+ */
+
+#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 ] \
+)
+
+#endif /* ARCNET_H */
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 2c78175..891fdae 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -19,6 +19,8 @@ CONFIG_DMA=y
 CONFIG_IDE_ISA=y
 CONFIG_IDE_PIIX=y
 CONFIG_NE2000_ISA=y
+CONFIG_COM90C65_ISA=y
+CONFIG_DE100_ISA=y
 CONFIG_PIIX_PCI=y
 CONFIG_SOUND=y
 CONFIG_HPET=y
diff --git a/hw/com90c65-isa.c b/hw/com90c65-isa.c
new file mode 100644
index 0000000..b1afb65
--- /dev/null
+++ b/hw/com90c65-isa.c
@@ -0,0 +1,131 @@
+/*
+ * QEMU ARCnet COM90C66 emulation
+ *
+ * This source file contains the ISA declaration of the network card. To work,
+ * it uses the generic code of the file com90c65.c.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "qdev.h"
+#include "net.h"
+#include "com90c65.h"
+
+/* default I/O addresses */
+#define IO_BASES_MAX 8
+static int io_bases[IO_BASES_MAX] = {0x260, 0x290, 0x2e0, 0x2f0, 0x300, 0x350, 0x380, 0x3e0};
+
+typedef struct ISACOM90C65State {
+    ISADevice dev;
+    uint32_t iobase;
+    uint32_t isairq;
+    uint32_t mmiobase;
+    uint32_t nodeid;
+    COM90C65State com90c65;
+} ISACOM90C65State;
+
+static void isa_com90c65_cleanup(VLANClientState *nc)
+{
+    COM90C65State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_com90c65_isa_info = {
+    .type = NET_CLIENT_TYPE_NIC,
+    .size = sizeof (NICState),
+    .can_receive = com90c65_can_receive,
+    .receive = com90c65_receive,
+    .cleanup = isa_com90c65_cleanup,
+};
+
+/* 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 ;
+}
+
+static int isa_com90c65_initfn(ISADevice *dev) {
+    ISACOM90C65State *isa = DO_UPCAST(ISACOM90C65State, dev, dev);
+    COM90C65State *s = &isa->com90c65;
+
+    if (s->c.vlan) {
+        dprintf("initialization (" COM90C65_DEVICE " in vlan%d)\n",
+                s->c.vlan->id);
+        dprintf("iobase=0x%x, irq=%d, mmiobase=0x%x, nodeid=%d\n",
+                isa->iobase, isa->isairq, isa->mmiobase, isa->nodeid);
+    }
+
+    if (com90c65_set_io_select(s, isa->iobase) != 0) {
+        eprintf("bad I/O address: %X\n", isa->iobase);
+        return -1;
+    }
+
+    com90c65_setup_io(s, REGISTERS_SIZE);
+    isa_register_ioport(dev, &s->io, isa->iobase);
+    com90c65_setup_mmio(s, MEM_SIZE);
+    memory_region_add_subregion_overlap(isa_address_space(dev),
+                                        isa->mmiobase, &s->mmio, 2);
+
+    isa_init_irq(dev, &s->irq, isa->isairq);
+
+    if (isa->nodeid > UCHAR_MAX)
+        qemu_macaddr_default_if_unset(&s->c.macaddr);
+    else {
+        memset(s->c.macaddr.a, 0, 5);
+        s->c.macaddr.a[5] = isa->nodeid;
+    }
+    s->mem_select = 0 ; /* not used */
+    s->node_id = s->c.macaddr.a[5];
+    com90c65_reset(s);
+
+    /* initialize read/write accesses to the registers for easy implementation of the read/write functions */
+    com90c65_registers_access_init(s);
+
+    /* register a hard reset used by qemu */
+    qemu_register_reset(com90c65_reset, s);
+
+    s->nic = qemu_new_nic(&net_com90c65_isa_info, &s->c,
+                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+
+    return 0;
+}
+
+static Property com90c65_isa_properties[] = {
+    DEFINE_PROP_HEX32("iobase", ISACOM90C65State, iobase, 0x2e0),
+    DEFINE_PROP_UINT32("irq", ISACOM90C65State, isairq, 5),
+    DEFINE_PROP_HEX32("mmiobase", ISACOM90C65State, mmiobase, 0xd0000),
+    DEFINE_PROP_UINT32("nodeid", ISACOM90C65State, nodeid, UCHAR_MAX + 1),
+    DEFINE_NIC_PROPERTIES(ISACOM90C65State, com90c65.c),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void isa_com90c65_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = isa_com90c65_initfn;
+    dc->props = com90c65_isa_properties;
+}
+
+static TypeInfo com90c65_isa_info = {
+    .name          = "com90c65_isa",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof (ISACOM90C65State),
+    .class_init    = isa_com90c65_class_initfn,
+};
+
+static void com90c65_isa_register_types(void)
+{
+    type_register_static(&com90c65_isa_info);
+}
+
+type_init(com90c65_isa_register_types)
diff --git a/hw/com90c65.c b/hw/com90c65.c
new file mode 100644
index 0000000..bc51d7c
--- /dev/null
+++ b/hw/com90c65.c
@@ -0,0 +1,483 @@
+/*
+ * QEMU ARCnet COM90C66 emulation
+ *
+ * This is a hardware NIC (Network Interface Card)
+ * emulation of the COM90C66 device family.
+ *
+ * It works on an (emulated) ISA bus. The code of this file should be generic
+ * however. ISA dependant code is in the source file com90c65-isa.c.
+ * 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 "com90c65.h"
+
+/* map the register variables with 2 arrays for read and write access */
+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;
+}
+
+/* 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");
+}
+
+/* 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 */
+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);
+
+    /* send to other clients of the vlan,
+     * it should be received by an arcnet layer */
+    qemu_send_packet(&s->nic->nc, buffer, size);
+}
+
+/* receive a packet from the vlan to the specified memory page */
+ssize_t com90c65_receive(VLANClientState *nc, const uint8_t *buffer, size_t size_)
+{
+    COM90C65State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+    int size = size_;
+
+    if (buffer == NULL || size <= 0)
+        return -1;
+
+    /* 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 -1;
+    }
+
+    if (DEBUG_RXTX)
+        dprintf("receive %d bytes\n", 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);
+    return size_;
+}
+
+/* the arcnet layer send a packet to the vlan only when the device is ready to receive */
+int com90c65_can_receive(VLANClientState *nc)
+{
+    COM90C65State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+    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 uint64_t com90c65_mmio_read(void *opaque, target_phys_addr_t addr, unsigned size) {
+    COM90C65State *s = opaque;
+    uint32_t value = 0;
+    int offset;
+    for (offset = 0; offset < size; offset ++)
+        value |= s->mem[addr + offset] << (offset << 3);
+    if (DEBUG_MM_RW)
+        dprintf("read %d bytes from memory 0x" TARGET_FMT_plx " (0x%x)\n",
+                size, addr, value);
+    return value;
+}
+
+/* write to memory mapped */
+static void com90c65_mmio_write(void *opaque, target_phys_addr_t addr, uint64_t data, unsigned size) {
+    COM90C65State *s = opaque;
+    uint32_t value = data;
+    int offset;
+    if (DEBUG_MM_RW)
+        dprintf("write %d bytes to memory: 0x" TARGET_FMT_plx " (0x%x)\n",
+                 size, addr, value);
+    for (offset = 0; offset < size; offset ++)
+        s->mem[addr + offset] = value >> (offset << 3) & 0xff;
+}
+
+static uint64_t com90c65_read(void *opaque, target_phys_addr_t addr,
+                              unsigned size)
+{
+    COM90C65State *s = opaque;
+
+    if (addr == OFFSET_DATA_LOW && size == 2) {
+        return com90c65_ioport_read16(s, addr);
+    } else if (addr >= OFFSET_RESET_BEGIN &&
+               addr <= OFFSET_RESET_END &&
+               size == 1) {
+        return com90c65_reset_on_read(s, addr);
+    } else if (addr < REGISTERS_SIZE && size == 1) {
+        return com90c65_ioport_read8(s, addr);
+    }
+    return ((uint64_t)1 << (size * 8)) - 1;
+}
+
+static void com90c65_write(void *opaque, target_phys_addr_t addr,
+                           uint64_t data, unsigned size)
+{
+    COM90C65State *s = opaque;
+
+    if (addr == OFFSET_DATA_LOW && size == 2) {
+        return com90c65_ioport_write16(s, addr, data);
+    } else if (addr >= OFFSET_RESET_BEGIN &&
+               addr <= OFFSET_RESET_END &&
+               size == 1) {
+        return com90c65_reset_on_write(s, addr, data);
+    } else if (addr < REGISTERS_SIZE && size == 1) {
+        return com90c65_ioport_write8(s, addr, data);
+    }
+}
+
+static const MemoryRegionOps com90c65_io_ops = {
+    .read = com90c65_read,
+    .write = com90c65_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+void com90c65_setup_io(COM90C65State *s, unsigned size)
+{
+    memory_region_init_io(&s->io, &com90c65_io_ops, s, "com90c65-io", size);
+}
+
+static const MemoryRegionOps com90c65_mmio_ops = {
+    .read = com90c65_mmio_read,
+    .write = com90c65_mmio_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+void com90c65_setup_mmio(COM90C65State *s, unsigned size)
+{
+    memory_region_init_io(&s->mmio, &com90c65_mmio_ops, s, "com90c65-mmio", size);
+}
diff --git a/hw/com90c65.h b/hw/com90c65.h
new file mode 100644
index 0000000..998e653
--- /dev/null
+++ b/hw/com90c65.h
@@ -0,0 +1,174 @@
+/*
+ * QEMU ARCnet COM90C66 emulation
+ */
+
+#include "arcnet.h"
+
+/* name of the device (debug usage) */
+#define COM90C65_DEVICE "com90c65"
+
+#ifdef COM90C65_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, COM90C65_DEVICE ": " fmt, ## args)
+#define not_implemented0(fmt, args...) eprintf(fmt ": not implemented\n", ## args)
+#ifdef COM90C65_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(COM90C65_DEVICE ": " fmt, ## args)
+#define BOOL_TEXT(state) (state ? "enable" : "disable")
+
+/* 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 */
+    MemoryRegion io;
+    MemoryRegion mmio;
+    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;
+    NICState *nic;
+    NICConf c;
+
+} COM90C65State;
+
+void com90c65_setup_io(COM90C65State *s, unsigned size);
+void com90c65_setup_mmio(COM90C65State *s, unsigned size);
+void com90c65_registers_access_init(COM90C65State *s);
+void com90c65_reset(void *user_data);
+int com90c65_can_receive(VLANClientState *nc);
+ssize_t com90c65_receive(VLANClientState *nc, const uint8_t *buffer, size_t size_);
diff --git a/hw/de100-isa.c b/hw/de100-isa.c
new file mode 100644
index 0000000..6255c08
--- /dev/null
+++ b/hw/de100-isa.c
@@ -0,0 +1,320 @@
+/*
+ * QEMU DE100 emulation
+ *
+ * The DE100 is an ISA DECnet network card. It is also known as DEC EtherWORKS
+ * LC Ethernet Controller.
+ */
+
+#include "isa.h"
+#include "net.h"
+#include "pcnet.h"
+
+#define DE100_DEVICE "de100"
+
+#ifdef DEBUG_DE100
+# define DPRINTF(fmt, args...) fprintf(stderr, DE100_DEVICE ": " fmt, ## args)
+#else
+# define DPRINTF(fmt, args...) do {} while (0)
+#endif /* !DEBUG_DE100 */
+
+#ifdef DEBUG_DE100_IO
+# define DPRINTF_IO(fmt, args...) fprintf(stderr, DE100_DEVICE ": " fmt, ## args)
+#else
+# define DPRINTF_IO(fmt, args...) do {} while (0)
+#endif /* !DEBUG_DE100_IO */
+
+#ifdef DEBUG_DE100_MMIO
+# define DPRINTF_MMIO(fmt, args...) fprintf(stderr, DE100_DEVICE ": " fmt, ## args)
+#else
+# define DPRINTF_MMIO(fmt, args...) do {} while (0)
+#endif /* !DEBUG_DE100_MMIO */
+
+/**
+ * NETWORK INTERFACE CSR (NI_CSR) bit definitions
+ * The NICSR is a register of the DE100 card.
+ */
+#define NICSR_TO        0x0100	/* Time Out for remote boot */
+#define NICSR_SHE       0x0080  /* SHadow memory Enable */
+#define NICSR_BS        0x0040  /* Bank Select */
+#define NICSR_BUF       0x0020	/* BUFfer size (1->32k, 0->64k) */
+#define NICSR_RBE       0x0010	/* Remote Boot Enable (1->net boot) */
+#define NICSR_AAC       0x0008  /* Address ROM Address Counter (1->enable) */
+#define NICSR_IM        0x0004	/* Interrupt Mask (1->mask) */
+#define NICSR_IEN       0x0002	/* Interrupt tristate ENable (1->enable) */
+#define NICSR_LED       0x0001	/* LED control */
+
+/**
+ * Device signature returned by the DE100 ISA card. This signature is checked by
+ * the host driver to recognise the device.
+ */
+#define DE100_SIGNATURE 0xAA5500FFUL
+
+
+typedef struct ISADE100State {
+    ISADevice dev;
+    PCNetState state;           /**< state of the Am7990 chip */
+
+    uint32_t iobase;            /**< Base address of I/O ports */
+    MemoryRegion io;
+
+    uint32_t mmiobase;          /**< Base address of I/O memory */
+    MemoryRegion mmio;
+    uint8_t mmiosize;           /**< Size of I/O mem (32 or 64 KB) */
+
+    uint32_t isairq;            /**< IRQ of the board */
+
+    uint16_t nicsr;             /**< Network interface CSR */
+    uint signature_pos;         /**< signature position used when reading the
+                                   signature of the card */
+    uint eth_addr_pos;          /**< prom position used when reading the MAC
+                                   address */
+} ISADE100State;
+
+/**
+ * Write data to an I/O port of the DE100 device
+ *
+ * @param opaque Device state (ISADE100State)
+ * @param addr address of the I/O port
+ * @param val value to write
+ * @param size size of the val (should be 2 bytes for this device)
+ */
+static void de100_io_write(void *opaque, target_phys_addr_t addr,
+                           uint64_t val, unsigned size)
+{
+    ISADE100State *isa = opaque;
+
+    DPRINTF_IO("I/O write: addr=0x%" PRIx64 ", val=0x%" PRIx64 "\n", addr, val);
+    switch (addr) {
+        case 0x00:
+            isa->nicsr = val & 0xffff;
+        case 0x04:
+        case 0x06:
+            pcnet_ioport_writew(&isa->state, addr - 0x04, val & 0xffff);
+            break;
+        default:
+            break;
+    }
+}
+
+/**
+ * Return the MAC address and the checksum byte by byte
+ *
+ * @param isa Device state
+ *
+ * @return a part of the MAC address or checksum
+ */
+static uint8_t de100_get_hw_addr(ISADE100State *isa)
+{
+    static int chksum = 0;
+    uint8_t val = 0;
+
+    if (isa->eth_addr_pos == 0)
+        chksum = 0;
+    if (isa->eth_addr_pos < 6) {
+        val = isa->state.prom[isa->eth_addr_pos];
+        chksum = chksum + (val << ((isa->eth_addr_pos % 2) * 8));
+        if ((isa->eth_addr_pos == 1) || (isa->eth_addr_pos == 3))
+            chksum <<= 1;
+        if (chksum > 0xffff)
+            chksum -= 0xffff;
+    } else if (isa->eth_addr_pos == 6) {
+        val = chksum & 0xff;
+    } else if (isa->eth_addr_pos == 7) {
+        val = (chksum >> 8) & 0xff;
+    }
+    isa->eth_addr_pos = (isa->eth_addr_pos + 1) % 8;
+    return val;
+}
+
+/**
+ * Read data from an I/O port of the DE100 device
+ *
+ * @param opaque Device state (ISADE100State)
+ * @param addr address of the I/O port
+ * @param size size of the return value (should be 2 bytes for this device)
+ *
+ * @return read value
+ */
+static uint64_t de100_io_read(void *opaque, target_phys_addr_t addr,
+                              unsigned size)
+{
+    ISADE100State *isa = opaque;
+    uint64_t val = -1;
+
+    switch (addr) {
+        case 0x00:
+            val = isa->nicsr;
+            break;
+        case 0x04:
+        case 0x06:
+            val = pcnet_ioport_readw(&isa->state, addr - 0x04) & 0xffff;
+            break;
+        case 0x0c:
+            val = (DE100_SIGNATURE >> ((isa->signature_pos % 4) * 8)) & 0xff;
+            ++isa->signature_pos;
+            break;
+        case 0x0d:
+            val = de100_get_hw_addr(isa);
+            break;
+        default:
+            break;
+    }
+    DPRINTF_IO("I/O read: addr=0x%" PRIx64 ", val=0x%" PRIx64 "\n", addr, val);
+    return val;
+}
+
+static const MemoryRegionOps de100_io_ops = {
+    .read = de100_io_read,
+    .write = de100_io_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 2,
+        .max_access_size = 2,
+    },
+};
+
+static void de100_isa_cleanup(VLANClientState *nc)
+{
+    PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    pcnet_common_cleanup(d);
+}
+
+static NetClientInfo net_de100_isa_info = {
+    .type = NET_CLIENT_TYPE_NIC,
+    .size = sizeof(NICState),
+    .can_receive = pcnet_can_receive,
+    .receive = pcnet_receive,
+    .link_status_changed = pcnet_set_link_status,
+    .cleanup = de100_isa_cleanup,
+};
+
+static const VMStateDescription de100_isa_vmstate = {
+    .name = "pcnet",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_STRUCT(state, ISADE100State, 0, vmstate_pcnet, PCNetState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/**
+ * Write data into the memory of the device
+ *
+ * @param dma_opaque base address of the mmio
+ * @param addr relative address of the data to write
+ * @param buf data to write
+ * @param len length of the data
+ * @param do_bswap not used
+ */
+static void isa_physical_memory_write(void *dma_opaque, target_phys_addr_t addr,
+                                      uint8_t *buf, int len, int do_bswap)
+{
+    uint32_t dmabase = *((uint32_t*) dma_opaque);
+
+    DPRINTF_MMIO("mmio write: addr=0x%" PRIx64 " len=%d\n", addr, len);
+    cpu_physical_memory_rw(dmabase | addr, buf, len, 1);
+}
+
+/**
+ * Read data from the memory of the device
+ *
+ * @param dma_opaque base address of the mmio
+ * @param addr relative address of the data to read
+ * @param buf data to read
+ * @param len length of the data
+ * @param do_bswap not used
+ */
+static void isa_physical_memory_read(void *dma_opaque, target_phys_addr_t addr,
+                                     uint8_t *buf, int len, int do_bswap)
+{
+    uint32_t dmabase = *((uint32_t*) dma_opaque);
+
+    cpu_physical_memory_rw(dmabase | addr, buf, len, 0);
+    DPRINTF_MMIO("mmio read: addr=0x%" PRIx64 " len=%d\n", addr, len);
+}
+
+/**
+ * Initialize the device
+ *
+ * @param dev ISA Device
+ *
+ * @return 0 on success
+ */
+static int de100_isa_init(ISADevice *dev)
+{
+    ISADE100State *isa = DO_UPCAST(ISADE100State, dev, dev);
+    PCNetState *s = &isa->state;
+
+    if (s->conf.vlan) {
+        DPRINTF("initialization (DE100 in vlan%d)\n",
+                s->conf.vlan->id);
+        DPRINTF("iobase=0x%x, irq=%d, mmiobase=0x%x, mmiosize=%d\n",
+                isa->iobase, isa->isairq, isa->mmiobase, isa->mmiosize);
+    }
+
+    /* Handler for memory-mapped I/O */
+    memory_region_init_io(&isa->io, &de100_io_ops, isa, "de100-io", 0x10);
+    isa_register_ioport(dev, &isa->io, isa->iobase);
+    memory_region_init_ram(&isa->mmio, "de100-mmio", isa->mmiosize * 1024);
+    vmstate_register_ram_global(&isa->mmio);
+    memory_region_add_subregion_overlap(isa_address_space(dev),
+                                        isa->mmiobase, &isa->mmio, 2);
+
+    isa_init_irq(dev, &s->irq, isa->isairq);
+    s->phys_mem_read = isa_physical_memory_read;
+    s->phys_mem_write = isa_physical_memory_write;
+    s->dma_opaque = &(isa->mmiobase);
+
+    isa->nicsr = (isa->mmiosize == 32) ? NICSR_BUF : 0;
+    isa->signature_pos = 0;
+    isa->eth_addr_pos = 0;
+
+    return pcnet_common_init(&dev->qdev, s, &net_de100_isa_info);
+}
+
+static void de100_isa_reset(DeviceState *dev)
+{
+    ISADE100State *isa = DO_UPCAST(ISADE100State, dev.qdev, dev);
+
+    pcnet_h_reset(&isa->state);
+    isa->nicsr = (isa->mmiosize == 32) ? NICSR_BUF : 0;
+    isa->signature_pos = 0;
+    isa->eth_addr_pos = 0;
+}
+
+static Property de100_isa_properties[] = {
+    DEFINE_PROP_HEX32("iobase", ISADE100State, iobase, 0x300),
+    DEFINE_PROP_UINT32("irq", ISADE100State, isairq, 2),
+    DEFINE_PROP_HEX32("mmiobase", ISADE100State, mmiobase, 0xd8000),
+    DEFINE_PROP_UINT8("mmiosize", ISADE100State, mmiosize, 32),
+    DEFINE_NIC_PROPERTIES(ISADE100State, state.conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void de100_isa_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+
+    ic->init = de100_isa_init;
+    dc->reset = de100_isa_reset;
+    dc->vmsd = &de100_isa_vmstate;
+    dc->props = de100_isa_properties;
+}
+
+static TypeInfo de100_isa_info = {
+    .name = "de100_isa",
+    .parent = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(ISADE100State),
+    .class_init = de100_isa_class_init,
+};
+
+static void de100_isa_register_types(void)
+{
+    type_register_static(&de100_isa_info);
+}
+
+type_init(de100_isa_register_types)
diff --git a/hw/isa-bus.c b/hw/isa-bus.c
index 5a43f03..7590b92 100644
--- a/hw/isa-bus.c
+++ b/hw/isa-bus.c
@@ -21,6 +21,7 @@
 #include "sysbus.h"
 #include "isa.h"
 #include "exec-memory.h"
+#include "net.h"
 
 static ISABus *isabus;
 target_phys_addr_t isa_mem_base = 0;
@@ -110,6 +111,56 @@ void isa_register_portio_list(ISADevice *dev, uint16_t start,
     portio_list_add(piolist, isabus->address_space_io, start);
 }
 
+/**
+ * List of ISA NIC.
+ * ISA names must end with "_isa" in order to be initialized.
+ */
+static const char * const isa_nic_models[] = {
+    "ne2k_isa",
+    "com90c65_isa",
+    "de100_isa",
+    NULL
+};
+
+/* Initialize an ISA NIC.  */
+ISADevice *isa_nic_init(ISABus *bus, NICInfo *nd, const char *default_model,
+                        bool (* const pc_prop_set_funcs[])(DeviceState *qdev))
+{
+    ISADevice *isa_dev;
+    DeviceState *dev;
+    int i;
+
+    i = qemu_find_nic_model(nd, isa_nic_models, default_model);
+    if (i < 0)
+        return NULL;
+
+    isa_dev = isa_try_create(bus, isa_nic_models[i]);
+    if (!isa_dev) {
+        return NULL;
+    }
+    dev = &isa_dev->qdev;
+    if (pc_prop_set_funcs[i] && !pc_prop_set_funcs[i](dev))
+        return NULL;
+    qdev_set_nic_properties(dev, nd);
+    if (qdev_init(dev) < 0)
+        return NULL;
+    return isa_dev;
+}
+
+ISADevice *isa_nic_init_nofail(ISABus *bus, NICInfo *nd, const char *default_model,
+                               bool (* const pc_prop_set_funcs[])(DeviceState *qdev))
+{
+    ISADevice *res;
+
+    if (qemu_show_nic_models(nd->model, isa_nic_models))
+        exit(0);
+
+    res = isa_nic_init(bus, nd, default_model, pc_prop_set_funcs);
+    if (!res)
+        exit(1);
+    return res;
+}
+
 static int isa_qdev_init(DeviceState *qdev)
 {
     ISADevice *dev = ISA_DEVICE(qdev);
diff --git a/hw/isa.h b/hw/isa.h
index f7bc4b5..9b11e33 100644
--- a/hw/isa.h
+++ b/hw/isa.h
@@ -46,6 +46,26 @@ ISADevice *isa_create(ISABus *bus, const char *name);
 ISADevice *isa_try_create(ISABus *bus, const char *name);
 ISADevice *isa_create_simple(ISABus *bus, const char *name);
 
+ISADevice *isa_nic_init(ISABus *bus, NICInfo *nd, const char *default_model,
+                        bool (* const pc_prop_set_funcs[])(DeviceState *qdev));
+/**
+ * isa_nic_init_nofail: Initialize a new NIC on the ISA bus.
+ *
+ * Create the new NIC represented by nd or, if unspecified, the NIC named
+ * default_model. The array pc_prop_set_funcs contains function pointers
+ * allowing to set NIC properties. This array must follow the same order as the
+ * array isa_nic_models.
+ *
+ * @bus: ISA bus
+ * @nd: ISA NIC to initialize
+ * @default_model: default model to use (if nb is not valid)
+ * @pc_prop_set_funcs: array of function pointers
+ *
+ * @return: An ISA device (or NULL on failure)
+ */
+ISADevice *isa_nic_init_nofail(ISABus *bus, NICInfo *nd, const char *default_model,
+                               bool (* const pc_prop_set_funcs[])(DeviceState *qdev));
+
 /**
  * isa_register_ioport: Install an I/O port region on the ISA bus.
  *
diff --git a/hw/pc.c b/hw/pc.c
index e81a06c..e9bd6d6 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -857,20 +857,76 @@ static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360,
                                               0x280, 0x380 };
 static const 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_mmio[COM90C65_NB_MAX] = { 0xd0000, 0xd0800, 0xd1000, 0xd1800, 0xd4000, 0xd4800, 0xd5000, 0xd5800 };
+
+#define DE100_NB_MAX 2
+static int de100_io[DE100_NB_MAX] = { 0x300, 0x200 };
+static int de100_irq[DE100_NB_MAX] = { 2, 5 };
+static int de100_mmiobase[DE100_NB_MAX] = { 0xd8000, 0xd0000 };
+static int de100_mmiosize[DE100_NB_MAX] = { 32, 32 };
+
 static const int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
 static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
 
-void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd)
+/* ne2000.c */
+static bool pc_prop_set_ne2k_isa(DeviceState *qdev)
 {
     static int nb_ne2k = 0;
 
     if (nb_ne2k == NE2000_NB_MAX)
-        return;
-    isa_ne2000_init(bus, ne2000_io[nb_ne2k],
-                    ne2000_irq[nb_ne2k], nd);
+        return false;
+
+    qdev_prop_set_uint32(qdev, "iobase", ne2000_io[nb_ne2k]);
+    qdev_prop_set_uint32(qdev, "irq",    ne2000_irq[nb_ne2k]);
     nb_ne2k++;
+    return true;
+}
+
+/* com90c65-isa.c */
+static bool pc_prop_set_com90c65_isa(DeviceState *qdev)
+{
+    static int nb_com90c65 = 0;
+
+    if (nb_com90c65 == COM90C65_NB_MAX)
+        return false;
+
+    qdev_prop_set_uint32(qdev, "iobase",   com90c65_io[nb_com90c65]);
+    qdev_prop_set_uint32(qdev, "irq",      com90c65_irq[nb_com90c65]);
+    qdev_prop_set_uint32(qdev, "mmiobase", com90c65_mmio[nb_com90c65]);
+    nb_com90c65++;
+    return true;
 }
 
+/* de100-isa.c */
+static bool pc_prop_set_de100_isa(DeviceState *qdev)
+{
+    static int nb_de100 = 0;
+
+    if (nb_de100 == DE100_NB_MAX)
+        return false;
+
+    qdev_prop_set_uint32(qdev, "iobase",  de100_io[nb_de100]);
+    qdev_prop_set_uint32(qdev, "irq",     de100_irq[nb_de100]);
+    qdev_prop_set_uint32(qdev, "mmiobase", de100_mmiobase[nb_de100]);
+    qdev_prop_set_uint8(qdev, "mmiosize", de100_mmiosize[nb_de100]);
+    nb_de100++;
+    return true;
+}
+
+/**
+ * Array of functions setting ISA NICs properties.
+ * This array must have the same size as isa_nic_models.
+ */
+bool (* const pc_prop_set_isa_functions[])(DeviceState *qdev) = {
+    pc_prop_set_ne2k_isa,
+    pc_prop_set_com90c65_isa,
+    pc_prop_set_de100_isa,
+    NULL
+};
+
 int cpu_is_bsp(CPUX86State *env)
 {
     /* We hard-wire the BSP to the first CPU. */
diff --git a/hw/pc.h b/hw/pc.h
index 74d3369..521044c 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -101,6 +101,7 @@ void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out);
 
 /* pc.c */
 extern int fd_bootchk;
+extern bool (* const pc_prop_set_isa_functions[])(DeviceState *qdev);
 
 void pc_register_ferr_irq(qemu_irq irq);
 void pc_acpi_smi_interrupt(void *opaque, int irq, int level);
@@ -120,7 +121,6 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
                           ISADevice **rtc_state,
                           ISADevice **floppy,
                           bool no_vmport);
-void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd);
 void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
                   const char *boot_device,
                   ISADevice *floppy, BusState *ide0, BusState *ide1,
@@ -197,24 +197,6 @@ int isa_vga_mm_init(target_phys_addr_t vram_base,
 /* cirrus_vga.c */
 DeviceState *pci_cirrus_vga_init(PCIBus *bus);
 
-/* ne2000.c */
-static inline bool isa_ne2000_init(ISABus *bus, int base, int irq, NICInfo *nd)
-{
-    ISADevice *dev;
-
-    qemu_check_nic_model(nd, "ne2k_isa");
-
-    dev = isa_try_create(bus, "ne2k_isa");
-    if (!dev) {
-        return false;
-    }
-    qdev_prop_set_uint32(&dev->qdev, "iobase", base);
-    qdev_prop_set_uint32(&dev->qdev, "irq",    irq);
-    qdev_set_nic_properties(&dev->qdev, nd);
-    qdev_init_nofail(&dev->qdev);
-    return true;
-}
-
 /* pc_sysfw.c */
 void pc_system_firmware_init(MemoryRegion *rom_memory);
 
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index a7aad4b..a4bd22f 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -237,9 +237,14 @@ static void pc_init1(MemoryRegion *system_memory,
 
     for(i = 0; i < nb_nics; i++) {
         NICInfo *nd = &nd_table[i];
-
-        if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0))
-            pc_init_ne2k_isa(isa_bus, nd);
+        size_t sz = 0;
+
+        if (nd->model)
+            sz = strlen(nd->model);
+        if (!pci_enabled ||
+            (nd->model && strcmp(nd->model + sz - 4, "_isa") == 0))
+            isa_nic_init_nofail(isa_bus, nd, "ne2k_isa",
+                                pc_prop_set_isa_functions);
         else
             pci_nic_init_nofail(nd, "e1000", NULL);
     }
diff --git a/net.c b/net.c
index 1922d8a..d6ea3d6 100644
--- a/net.c
+++ b/net.c
@@ -31,6 +31,7 @@
 #include "net/slirp.h"
 #include "net/vde.h"
 #include "net/util.h"
+#include "net/user-arcnet.h"
 #include "monitor.h"
 #include "qemu-common.h"
 #include "qemu_socket.h"
@@ -1083,6 +1084,19 @@ static const struct {
         },
     },
 #endif /* CONFIG_NET_BRIDGE */
+    [NET_CLIENT_TYPE_USER_ARCNET] = {
+        .type = "user-arcnet",
+        .init = net_init_user_arcnet,
+        .desc = {
+            NET_COMMON_PARAMS_DESC,
+            {
+                .name = "ifname",
+                .type = QEMU_OPT_STRING,
+                .help = "interface name",
+            },
+            { /* end of list */ }
+        },
+    },
 };
 
 int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
@@ -1387,6 +1401,7 @@ void net_check_clients(void)
             case NET_CLIENT_TYPE_TAP:
             case NET_CLIENT_TYPE_SOCKET:
             case NET_CLIENT_TYPE_VDE:
+            case NET_CLIENT_TYPE_USER_ARCNET:
                 has_host_dev = 1;
                 break;
             default: ;
diff --git a/net.h b/net.h
index 64993b4..68d029b 100644
--- a/net.h
+++ b/net.h
@@ -38,6 +38,7 @@ typedef enum {
     NET_CLIENT_TYPE_VDE,
     NET_CLIENT_TYPE_DUMP,
     NET_CLIENT_TYPE_BRIDGE,
+    NET_CLIENT_TYPE_USER_ARCNET,
 
     NET_CLIENT_TYPE_MAX
 } net_client_type;
diff --git a/net/user-arcnet.c b/net/user-arcnet.c
new file mode 100644
index 0000000..b5c4f6d
--- /dev/null
+++ b/net/user-arcnet.c
@@ -0,0 +1,187 @@
+/*
+ * QEMU ARCNet user mode
+ *
+ * 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 <err.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if_arp.h>
+#include <linux/if_arcnet.h>
+
+#include "qemu-char.h"
+
+#include "user-arcnet.h"
+#include "arcnet.h"
+
+#ifdef USER_ARCNET_DEBUG
+/* set it to 1 to print send/receive events */
+# define USER_ARCNET_DEBUG_RXTX 0
+#else
+# define USER_ARCNET_DEBUG_RXTX 0
+#endif
+
+#ifdef USER_ARCNET_DEBUG
+# define dprintf0(fmt, args...) fprintf(stderr, fmt, ## args)
+#else
+# define dprintf0(fmt, args...) do {} while (0)
+#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 UserArcnetState {
+    VLANClientState nc;
+    int fd;
+    struct sockaddr address;
+} UserArcnetState;
+
+/* send a packet from the emulator vlan to the host */
+static ssize_t net_user_arcnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+    UserArcnetState *s = DO_UPCAST(UserArcnetState, nc, nc);
+    ArcnetPacket *linux_buf = (ArcnetPacket*)buf;
+    int data_length, length;
+    ssize_t sent;
+
+    /* ignore bad packets */
+    if (buf == NULL || size <= 0 || size != ARCNET_SIZE(buf)) {
+        return -1;
+    }
+
+    /* convert from hardware to linux format (see linux/if_arcnet.h),
+     * move data to the beginning of the buffer */
+    data_length = ARCNET_DATA_LENGTH(buf);
+    memmove(linux_buf->soft.raw, ARCNET_DATA(buf), data_length);
+    length = data_length + (linux_buf->soft.raw - (uint8_t*) linux_buf);
+
+    if (USER_ARCNET_DEBUG_RXTX)
+        dprintf("send %d bytes\n", length);
+
+    /* send raw packet to the predefined interface */
+    sent = sendto(s->fd, buf, length, 0, &s->address, sizeof (s->address));
+    if (sent != length) {
+        dperror();
+        return -1;
+    }
+    return sent;
+}
+
+/* cannot receive packet if the device is not ready to receive */
+static int net_user_arcnet_can_send(void *opaque)
+{
+    UserArcnetState *s = opaque;
+    return qemu_can_send_packet(&s->nc);
+}
+
+/* receive a packet from the host to the emulator vlan */
+static void net_user_arcnet_send(void *opaque)
+{
+    UserArcnetState *s = opaque;
+    uint8_t buf[ARCNET_MAX_SIZE];
+    ArcnetPacket *linux_buf = (ArcnetPacket*)buf;
+    ssize_t received;
+    int data_length, size;
+
+    /* receive raw packet from the predefined interface */
+    received = qemu_recv(s->fd, buf, ARCNET_MAX_SIZE, 0);
+    if (received <= 0) {
+        dperror();
+        return;
+    }
+
+    if (USER_ARCNET_DEBUG_RXTX)
+        dprintf("receive %d bytes\n", received);
+
+    /* get data length */
+    data_length = received - (linux_buf->soft.raw - (uint8_t*) linux_buf);
+    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 */
+    buf[ARCNET_SHORT_START] = 0;
+    buf[ARCNET_LONG_START] = 0;
+    buf[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(buf), linux_buf->soft.raw, data_length);
+
+    /* send packet to the vlan */
+    qemu_send_packet(&s->nc, buf, size);
+}
+
+static void net_user_arcnet_cleanup(VLANClientState *nc)
+{
+    UserArcnetState *s = DO_UPCAST(UserArcnetState, nc, nc);
+    qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+    close(s->fd);
+}
+
+static NetClientInfo net_user_arcnet_info = {
+    .type = NET_CLIENT_TYPE_USER_ARCNET,
+    .size = sizeof (UserArcnetState),
+    .receive = net_user_arcnet_receive,
+    .cleanup = net_user_arcnet_cleanup,
+};
+
+int net_init_user_arcnet(QemuOpts *opts, Monitor *mon,
+                         const char *name, VLANState *vlan)
+{
+    VLANClientState *nc;
+    UserArcnetState *s;
+    const char *ifname;
+
+    if ((ifname = qemu_opt_get(opts, "ifname")) == NULL)
+        ifname = "arc0";
+
+    dprintf("initialization (%s in vlan%d)\n", ifname, vlan->id);
+
+    nc = qemu_new_net_client(&net_user_arcnet_info, vlan, NULL, "user-arcnet", name);
+
+    s = DO_UPCAST(UserArcnetState, nc, nc);
+
+    /* open a raw socket */
+    s->fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARCNET));
+    if (s->fd < 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 */
+    if (bind(s->fd, &s->address, sizeof (s->address))) {
+        dperror("bind on %s: ", ifname);
+        return -1;
+    }
+
+    /* set a handler to receive data from the ARCnet network */
+    qemu_set_fd_handler2(s->fd, net_user_arcnet_can_send,
+                         net_user_arcnet_send, NULL, s);
+
+    snprintf(nc->info_str, sizeof (nc->info_str), "arcnet user mode");
+    dprintf("vlan=%d, interface=%s, fd=%d\n", vlan->id, ifname, s->fd);
+
+    return 0;
+}
diff --git a/net/user-arcnet.h b/net/user-arcnet.h
new file mode 100644
index 0000000..a4a3b9c
--- /dev/null
+++ b/net/user-arcnet.h
@@ -0,0 +1,14 @@
+/*
+ * QEMU ARCNet user mode
+ */
+
+#ifndef QEMU_NET_USER_ARCNET_H
+#define QEMU_NET_USER_ARCNET_H
+
+#include "net.h"
+#include "qemu-common.h"
+
+int net_init_user_arcnet(QemuOpts *opts, Monitor *mon,
+                         const char *name, VLANState *vlan);
+
+#endif /* QEMU_NET_USER_ARCNET_H */
