diff --git a/Makefile.target b/Makefile.target
index ab774e5..689cbf0 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -197,7 +197,7 @@ obj-i386-y += fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
 obj-i386-y += cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o
 obj-i386-y += usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
 obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
-obj-i386-y += ne2000-isa.o
+obj-i386-y += ne2000-isa.o debugcon.o
 
 # shared objects
 obj-ppc-y = ppc.o ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/macio.o
diff --git a/hw/debugcon.c b/hw/debugcon.c
new file mode 100644
index 0000000..6a724bb
--- /dev/null
+++ b/hw/debugcon.c
@@ -0,0 +1,189 @@
+/*
+ * QEMU Bochs-style debug console ("port E9") emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ * Copyright (c) Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "qemu-char.h"
+#include "isa.h"
+#include "pc.h"
+
+//#define DEBUG_DEBUGCON
+
+struct DebugconState {
+    CharDriverState *chr;
+};
+
+typedef struct ISADebugconState {
+    ISADevice dev;
+    uint32_t index;
+    uint32_t iobase;
+    DebugconState state;
+} ISADebugconState;
+
+static void debugcon_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    DebugconState *s = opaque;
+    unsigned char ch = val;
+
+#ifdef DEBUG_DEBUGCON
+    printf("debugcon: write addr=0x%04x val=0x%02x\n", addr, val);
+#endif
+
+    qemu_chr_write(s->chr, &ch, 1);
+}
+
+static void debugcon_init_core(DebugconState *s)
+{
+    if (!s->chr) {
+        fprintf(stderr, "Can't create debugcon device, empty char device\n");
+	exit(1);
+    }
+
+    qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, s);
+}
+
+static const uint16_t isa_debugcon_iobase[MAX_DEBUGCON_PORTS] = { 0xe9 };
+
+static int debugcon_isa_initfn(ISADevice *dev)
+{
+    static int index;
+    ISADebugconState *isa = DO_UPCAST(ISADebugconState, dev, dev);
+    DebugconState *s = &isa->state;
+
+    if (isa->index == -1)
+        isa->index = index;
+    if (isa->index >= MAX_DEBUGCON_PORTS)
+        return -1;
+    if (isa->iobase == -1)
+	isa->iobase = isa_debugcon_iobase[isa->index];
+    index++;
+
+    debugcon_init_core(s);
+    register_ioport_write(isa->iobase, 1, 1, debugcon_ioport_write, s);
+    return 0;
+}
+
+DebugconState *debugcon_isa_init(int index, CharDriverState *chr)
+{
+    ISADevice *dev;
+
+    dev = isa_create("isa-debugcon");
+    qdev_prop_set_chr(&dev->qdev, "chardev", chr);
+    if (qdev_init(&dev->qdev) < 0)
+        return NULL;
+    return &DO_UPCAST(ISADebugconState, dev, dev)->state;
+}
+
+#if 0 /* Non-ISA interfaces, available for the future */
+
+DebugconState *debugcon_init(int base, qemu_irq irq, int baudbase,
+                         CharDriverState *chr)
+{
+    DebugconState *s;
+
+    s = qemu_mallocz(sizeof(DebugconState));
+
+    s->chr = chr;
+    debugcon_init_core(s);
+
+    register_ioport_write(base, 8, 1, debugcon_ioport_write, s);
+    return s;
+}
+
+/* Memory mapped interface */
+static void debugcon_mm_writeb(void *opaque, target_phys_addr_t addr,
+                             uint32_t value)
+{
+    DebugconState *s = opaque;
+
+    debugcon_ioport_write(s, addr >> s->it_shift, value & 0xFF);
+}
+
+static void debugcon_mm_writew(void *opaque, target_phys_addr_t addr,
+                             uint32_t value)
+{
+    DebugconState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+    value = bswap16(value);
+#endif
+    debugcon_ioport_write(s, addr >> s->it_shift, value & 0xFFFF);
+}
+
+static void debugcon_mm_writel(void *opaque, target_phys_addr_t addr,
+                             uint32_t value)
+{
+    DebugconState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+    value = bswap32(value);
+#endif
+    debugcon_ioport_write(s, addr >> s->it_shift, value);
+}
+
+static CPUWriteMemoryFunc * const debugcon_mm_write[] = {
+    &debugcon_mm_writeb,
+    &debugcon_mm_writew,
+    &debugcon_mm_writel,
+};
+
+DebugconState *debugcon_mm_init (target_phys_addr_t base, int it_shift,
+                             qemu_irq irq, int baudbase,
+                             CharDriverState *chr, int ioregister)
+{
+    DebugconState *s;
+    int s_io_memory;
+
+    s = qemu_mallocz(sizeof(DebugconState));
+
+    s->chr = chr;
+
+    debugcon_init_core(s);
+
+    if (ioregister) {
+        s_io_memory = cpu_register_io_memory(NULL, debugcon_mm_write, s);
+        cpu_register_physical_memory(base, 1 << it_shift, s_io_memory);
+    }
+    return s;
+}
+
+#endif /* Non-ISA interfaces */
+
+static ISADeviceInfo debugcon_isa_info = {
+    .qdev.name  = "isa-debugcon",
+    .qdev.size  = sizeof(ISADebugconState),
+    .init       = debugcon_isa_initfn,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_HEX32("index",  ISADebugconState, index,   -1),
+        DEFINE_PROP_HEX32("iobase", ISADebugconState, iobase,  -1),
+        DEFINE_PROP_CHR("chardev",  ISADebugconState, state.chr),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static void debugcon_register_devices(void)
+{
+    isa_qdev_register(&debugcon_isa_info);
+}
+
+device_init(debugcon_register_devices)
diff --git a/hw/pc.c b/hw/pc.c
index 7c791c4..3957c78 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -1159,6 +1159,12 @@ static void pc_init1(ram_addr_t ram_size,
         }
     }
 
+    for(i = 0; i < MAX_DEBUGCON_PORTS; i++) {
+        if (debugcon_hds[i]) {
+	    debugcon_isa_init(i, debugcon_hds[i]);
+        }
+    }
+
     for(i = 0; i < nb_nics; i++) {
         NICInfo *nd = &nd_table[i];
 
diff --git a/hw/pc.h b/hw/pc.h
index 03ffc91..4048be5 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -21,6 +21,10 @@ typedef struct ParallelState ParallelState;
 ParallelState *parallel_init(int index, CharDriverState *chr);
 ParallelState *parallel_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, CharDriverState *chr);
 
+/* debugcon.c */
+
+DebugconState *debugcon_isa_init(int index, CharDriverState *chr);
+
 /* i8259.c */
 
 typedef struct PicState2 PicState2;
diff --git a/qemu-common.h b/qemu-common.h
index b779cfe..9dd3ca8 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -208,6 +208,7 @@ typedef struct uWireSlave uWireSlave;
 typedef struct I2SCodec I2SCodec;
 typedef struct DeviceState DeviceState;
 typedef struct SSIBus SSIBus;
+typedef struct DebugconState DebugconState;
 
 /* CPU save/load.  */
 void cpu_save(QEMUFile *f, void *opaque);
diff --git a/qemu-options.hx b/qemu-options.hx
index b65fd74..92ade30 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1586,6 +1586,17 @@ The default device is @code{vc} in graphical mode and @code{stdio} in
 non graphical mode.
 ETEXI
 
+DEF("debugcon", HAS_ARG, QEMU_OPTION_debugcon, \
+    "-debugcon dev   redirect the debug console to char device 'dev'\n")
+STEXI
+@item -monitor @var{dev}
+Redirect the debug console to host device @var{dev} (same devices as the
+serial port).  The debug console is an I/O port which is typically port
+0xe9; writing to that I/O port sends output to this device.
+The default device is @code{vc} in graphical mode and @code{stdio} in
+non graphical mode.
+ETEXI
+
 DEF("pidfile", HAS_ARG, QEMU_OPTION_pidfile, \
     "-pidfile file   write PID to 'file'\n")
 STEXI
diff --git a/sysemu.h b/sysemu.h
index b1887ef..e56b111 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -225,6 +225,12 @@ extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
 
 extern CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
 
+/* debugging ports */
+
+#define MAX_DEBUGCON_PORTS 1
+
+extern CharDriverState *debugcon_hds[MAX_DEBUGCON_PORTS];
+
 #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
 
 #ifdef HAS_AUDIO
diff --git a/vl.c b/vl.c
index c3f3c8f..978f53b 100644
--- a/vl.c
+++ b/vl.c
@@ -212,6 +212,7 @@ int no_quit = 0;
 CharDriverState *serial_hds[MAX_SERIAL_PORTS];
 CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
 CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
+CharDriverState *debugcon_hds[MAX_DEBUGCON_PORTS];
 #ifdef TARGET_I386
 int win2k_install_hack = 0;
 int rtc_td_hack = 0;
@@ -4598,6 +4599,8 @@ int main(int argc, char **argv, char **envp)
     int parallel_device_index;
     const char *virtio_consoles[MAX_VIRTIO_CONSOLES];
     int virtio_console_index;
+    const char *debugcon_devices[MAX_DEBUGCON_PORTS];
+    int debugcon_device_index;
     const char *loadvm = NULL;
     QEMUMachine *machine;
     const char *cpu_model;
@@ -4683,6 +4686,11 @@ int main(int argc, char **argv, char **envp)
     }
     monitor_device_index = 0;
 
+    debugcon_devices[0] = "vc:80Cx24C";
+    for(i = 1; i < MAX_DEBUGCON_PORTS; i++)
+        debugcon_devices[i] = NULL;
+    debugcon_device_index = 0;
+
     for (i = 0; i < MAX_NODES; i++) {
         node_mem[i] = 0;
         node_cpumask[i] = 0;
@@ -5123,6 +5131,14 @@ int main(int argc, char **argv, char **envp)
                 serial_devices[serial_device_index] = optarg;
                 serial_device_index++;
                 break;
+	    case QEMU_OPTION_debugcon:
+                if (debugcon_device_index >= MAX_DEBUGCON_PORTS) {
+                    fprintf(stderr, "qemu: too many debugcon ports\n");
+                    exit(1);
+                }
+                debugcon_devices[debugcon_device_index] = optarg;
+                debugcon_device_index++;
+                break;
             case QEMU_OPTION_watchdog:
                 if (watchdog) {
                     fprintf(stderr,
@@ -5430,6 +5446,8 @@ int main(int argc, char **argv, char **envp)
        if (strncmp(monitor_devices[0], "vc", 2) == 0) {
            monitor_devices[0] = "stdio";
        }
+       if (debugcon_device_index == 0)
+           debugcon_devices[0] = "stdio";
     }
 
 #ifndef _WIN32
@@ -5691,6 +5709,20 @@ int main(int argc, char **argv, char **envp)
         }
     }
 
+    for(i = 0; i < MAX_DEBUGCON_PORTS; i++) {
+        const char *devname = debugcon_devices[i];
+        if (devname && strcmp(devname, "none")) {
+            char label[32];
+            snprintf(label, sizeof(label), "debugcon%d", i);
+            debugcon_hds[i] = qemu_chr_open(label, devname, NULL);
+            if (!debugcon_hds[i]) {
+                fprintf(stderr, "qemu: could not open debugcon device '%s': %s\n",
+                        devname, strerror(errno));
+                exit(1);
+            }
+        }
+    }
+
     module_call_init(MODULE_INIT_DEVICE);
 
     if (watchdog) {
@@ -5826,6 +5858,14 @@ int main(int argc, char **argv, char **envp)
         }
     }
 
+    for(i = 0; i < MAX_DEBUGCON_PORTS; i++) {
+        const char *devname = debugcon_devices[i];
+        if (devname && strcmp(devname, "none")) {
+            if (strstart(devname, "vc", 0))
+                qemu_chr_printf(debugcon_hds[i], "debugcon%d console\r\n", i);
+        }
+    }
+
     if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) {
         fprintf(stderr, "qemu: could not open gdbserver on device '%s'\n",
                 gdbstub_dev);
