diff --git a/Makefile.target b/Makefile.target
index 2f842c1..7f69f72 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -366,6 +366,7 @@ obj-s390x-y = s390-virtio-bus.o s390-virtio.o
 
 obj-alpha-y += i8259.o mc146818rtc.o
 obj-alpha-y += vga.o cirrus_vga.o
+obj-alpha-y += alpha_pci.o alpha_dp264.o alpha_typhoon.o
 
 main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
 
diff --git a/hw/alpha_dp264.c b/hw/alpha_dp264.c
new file mode 100644
index 0000000..c061b0a
--- /dev/null
+++ b/hw/alpha_dp264.c
@@ -0,0 +1,177 @@
+/*
+ * QEMU Alpha DP264/CLIPPER hardware system emulator.
+ *
+ * Choose CLIPPER IRQ mappings over, say, DP264, MONET, or WEBBRICK
+ * variants because CLIPPER doesn't have an SMC669 SuperIO controler
+ * that we need to emulate as well.
+ */
+
+#include "hw.h"
+#include "elf.h"
+#include "loader.h"
+#include "boards.h"
+#include "alpha_sys.h"
+#include "sysemu.h"
+#include "mc146818rtc.h"
+
+
+static uint64_t cpu_alpha_superpage_to_phys(void *opaque, uint64_t addr)
+{
+    if (((addr >> 41) & 3) == 2) {
+        addr &= 0xffffffffffull;
+    }
+    return addr;
+}
+
+/* Note that there are at least 3 viewpoints of IRQ numbers on Alpha systems.
+    (0) The dev_irq_n lines into the cpu, which we totally ignore,
+    (1) The DRIR lines in the typhoon chipset,
+    (2) The "vector" aka mangled interrupt number reported by SRM PALcode,
+    (3) The interrupt number assigned by the kernel.
+   The following function is concerned with (1) only.  */
+
+static int clipper_pci_map_irq(PCIDevice *d, int irq_num)
+{
+    int slot = d->devfn >> 3;
+
+    assert(irq_num >= 0 && irq_num <= 3);
+
+    return (slot + 1) * 4 + irq_num;
+}
+
+static void clipper_init(ram_addr_t ram_size,
+                         const char *boot_device,
+                         const char *kernel_filename,
+                         const char *kernel_cmdline,
+                         const char *initrd_filename,
+                         const char *cpu_model)
+{
+    CPUState *cpus[4];
+    ram_addr_t ram_offset;
+    PCIBus *pci_bus;
+    ISABus *isa_bus;
+    qemu_irq isa_pci_irq, rtc_irq, *isa_irqs;
+    long size, i;
+    const char *palcode_filename;
+    uint64_t palcode_entry, palcode_low, palcode_high;
+    uint64_t kernel_entry, kernel_low, kernel_high;
+
+    /* Create up to 4 cpus.  */
+    memset(cpus, 0, sizeof(cpus));
+    for (i = 0; i < smp_cpus; ++i) {
+        cpus[i] = cpu_init(cpu_model ? cpu_model : "ev67");
+    }
+
+    cpus[0]->trap_arg0 = ram_size;
+    cpus[0]->trap_arg1 = 0;
+    cpus[0]->trap_arg2 = smp_cpus;
+
+    ram_offset = qemu_ram_alloc(NULL, "ram", ram_size);
+    cpu_register_physical_memory(0, ram_size, ram_offset);
+
+    /* Init the chipset.  */
+    pci_bus = typhoon_init(&isa_pci_irq, &rtc_irq, cpus, clipper_pci_map_irq);
+
+    /* Init the ISA bus.  */
+    isa_bus = isa_bus_new(NULL);
+    isa_mem_base = pci_to_cpu_addr(pci_bus, 0);
+
+    isa_irqs = i8259_init(isa_pci_irq);
+    isa_bus_irqs(isa_irqs);
+
+    rtc_init(1980, rtc_irq);
+    pit_init(0x40, 0);
+
+    /* VGA setup.  Don't bother loading the bios.  */
+    pci_vga_init(pci_bus);
+
+    /* Serial code setup.  */
+    for (i = 0; i < MAX_SERIAL_PORTS; ++i) {
+        if (serial_hds[i]) {
+            serial_isa_init(i, serial_hds[i]);
+        }
+    }
+
+    /* Network setup.  e1000 is good enough, failing Tulip support.  */
+    for (i = 0; i < nb_nics; i++) {
+        pci_nic_init_nofail(&nd_table[i], "e1000", NULL);
+    }
+
+    /* Load PALcode.  Given that this is not "real" cpu palcode,
+       but one explicitly written for the emulation, we might as
+       well load it directly from and ELF image.  */
+    palcode_filename = (bios_name ? bios_name : "palcode-clipper");
+    palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, palcode_filename);
+    if (palcode_filename == NULL) {
+        hw_error("no palcode provided\n");
+        exit(1);
+    }
+    size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys,
+                    NULL, &palcode_entry, &palcode_low, &palcode_high,
+                    0, EM_ALPHA, 0);
+    if (size < 0) {
+        hw_error("could not load palcode '%s'\n", palcode_filename);
+        exit(1);
+    }
+
+    /* Start all cpus at the PALcode RESET entry point.  */
+    for (i = 0; i < smp_cpus; ++i) {
+        cpus[i]->pc = palcode_entry;
+        cpus[i]->palbr = palcode_entry;
+    }
+
+    /* Load a kernel.  */
+    if (kernel_filename) {
+        uint64_t param_offset;
+
+        size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys,
+                        NULL, &kernel_entry, &kernel_low, &kernel_high,
+                        0, EM_ALPHA, 0);
+        if (size < 0) {
+            hw_error("could not load kernel '%s'\n", kernel_filename);
+            exit(1);
+        }
+
+        cpus[0]->trap_arg1 = kernel_entry;
+
+        param_offset = kernel_low - 0x6000;
+
+        if (kernel_cmdline) {
+            pstrcpy_targphys("cmdline", param_offset, 0x100, kernel_cmdline);
+        }
+
+        if (initrd_filename) {
+            long initrd_base, initrd_size;
+
+            initrd_size = get_image_size(initrd_filename);
+            if (initrd_size < 0) {
+                hw_error("could not load initial ram disk '%s'\n",
+                         initrd_filename);
+                exit(1);
+            }
+
+            /* Put the initrd image as high in memory as possible.  */
+            initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK;
+            load_image_targphys(initrd_filename, initrd_base,
+                                ram_size - initrd_base);
+
+            stq_phys(param_offset + 0x100, initrd_base + 0xfffffc0000000000UL);
+            stq_phys(param_offset + 0x108, initrd_size);
+        }
+    }
+}
+
+static QEMUMachine clipper_machine = {
+    .name = "clipper",
+    .desc = "Alpha DP264/CLIPPER",
+    .init = clipper_init,
+    .max_cpus = 4,
+    .is_default = 1,
+};
+
+static void clipper_machine_init(void)
+{
+    qemu_register_machine(&clipper_machine);
+}
+
+machine_init(clipper_machine_init);
diff --git a/hw/alpha_pci.c b/hw/alpha_pci.c
new file mode 100644
index 0000000..d0d727c
--- /dev/null
+++ b/hw/alpha_pci.c
@@ -0,0 +1,327 @@
+/* There's nothing in here that's Alpha specific, really.  */
+
+#include "config.h"
+#include "alpha_sys.h"
+#include "qemu-log.h"
+
+
+/* PCI IO reads, to byte-word addressable memory.  */
+/* ??? Doesn't handle multiple PCI busses.  */
+
+static uint32_t bw_io_readb(void *opaque, target_phys_addr_t addr)
+{
+    return cpu_inb(addr);
+}
+
+static uint32_t bw_io_readw(void *opaque, target_phys_addr_t addr)
+{
+    return cpu_inw(addr);
+}
+
+static uint32_t bw_io_readl(void *opaque, target_phys_addr_t addr)
+{
+    return cpu_inl(addr);
+}
+
+/* PCI IO writes, to byte-word addressable memory.  */
+/* ??? Doesn't handle multiple PCI busses.  */
+
+static void bw_io_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    cpu_outb(addr, val);
+}
+
+static void bw_io_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    cpu_outw(addr, val);
+}
+
+static void bw_io_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    cpu_outl(addr, val);
+}
+
+CPUReadMemoryFunc * const alpha_pci_bw_io_reads[] = {
+    bw_io_readb,
+    bw_io_readw,
+    bw_io_readl,
+};
+
+CPUWriteMemoryFunc * const alpha_pci_bw_io_writes[] = {
+    bw_io_writeb,
+    bw_io_writew,
+    bw_io_writel,
+};
+
+/* PCI config space reads, to byte-word addressable memory.  */
+static uint32_t bw_conf1_readb(void *opaque, target_phys_addr_t addr)
+{
+    PCIBus *b = opaque;
+    return pci_data_read(b, addr, 1);
+}
+
+static uint32_t bw_conf1_readw(void *opaque, target_phys_addr_t addr)
+{
+    PCIBus *b = opaque;
+    return pci_data_read(b, addr, 2);
+}
+
+static uint32_t bw_conf1_readl(void *opaque, target_phys_addr_t addr)
+{
+    PCIBus *b = opaque;
+    return pci_data_read(b, addr, 4);
+}
+
+/* PCI config space writes, to byte-word addressable memory.  */
+static void bw_conf1_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    PCIBus *b = opaque;
+    pci_data_write(b, addr, val, 1);
+}
+
+static void bw_conf1_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    PCIBus *b = opaque;
+    pci_data_write(b, addr, val, 2);
+}
+
+static void bw_conf1_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    PCIBus *b = opaque;
+    pci_data_write(b, addr, val, 4);
+}
+
+CPUReadMemoryFunc * const alpha_pci_bw_conf1_reads[] = {
+    bw_conf1_readb,
+    bw_conf1_readw,
+    bw_conf1_readl,
+};
+
+CPUWriteMemoryFunc * const alpha_pci_bw_conf1_writes[] = {
+    bw_conf1_writeb,
+    bw_conf1_writew,
+    bw_conf1_writel,
+};
+
+/* PCI MEM access to dense (but not byte-word addressable) memory.  */
+static uint32_t dense_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    PCIBus *b = opaque;
+    return ldl_phys(pci_to_cpu_addr(b, addr));
+}
+
+static void dense_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t v)
+{
+    PCIBus *b = opaque;
+    stl_phys(pci_to_cpu_addr(b, addr), v);
+}
+
+CPUReadMemoryFunc * const alpha_pci_dense_mem_reads[] = {
+    unassigned_mem_readb,
+    unassigned_mem_readw,
+    dense_mem_readl,
+};
+
+CPUWriteMemoryFunc * const alpha_pci_dense_mem_writes[] = {
+    unassigned_mem_writeb,
+    unassigned_mem_writew,
+    dense_mem_writel,
+};
+
+/* PCI IO to sparse memory.  These are helper routines, which expect that the
+   relevant HAE has already been prepended to ADDR by the core-specific
+   routine that is actually registered with the memory region.  */
+
+uint32_t alpha_sparse_io_read(target_phys_addr_t addr)
+{
+    int size = (addr >> 3) & 3;
+    uint32_t val;
+
+    addr >>= 5;
+    switch (size) {
+    case 0:
+        /* byte access */
+        val = cpu_inb(addr);
+        break;
+    case 1:
+        /* word access */
+        val = cpu_inw(addr);
+        break;
+    case 2:
+        /* tri-byte access; apparently possible with real pci lines.  */
+        qemu_log("pci: tri-byte io read");
+        return ~0u;
+    default:
+        /* long access */
+        return cpu_inl(addr);
+    }
+
+    val <<= (addr & 3) * 8;
+    return val;
+}
+
+void alpha_sparse_io_write(target_phys_addr_t addr, uint32_t val)
+{
+    int size = (addr >> 3) & 3;
+
+    addr >>= 5;
+    switch (size) {
+    case 0:
+        /* byte access */
+        val >>= (addr & 3) * 8;
+        cpu_outb(addr, val);
+        break;
+    case 1:
+        /* word access */
+        val >>= (addr & 3) * 8;
+        cpu_outw(addr, val);
+        break;
+    case 2:
+        /* tri-byte access; apparently possible with real pci lines.  */
+        qemu_log("pci: tri-byte io write");
+        break;
+    default:
+        /* long access */
+        cpu_outl(addr, val);
+        break;
+    }
+}
+
+uint32_t alpha_sparse_mem_read(PCIBus *b, target_phys_addr_t addr)
+{
+    int size = (addr >> 3) & 3;
+    uint32_t val;
+
+    addr = pci_to_cpu_addr(b, addr >> 5);
+    switch (size) {
+    case 0:
+        /* byte access */
+        val = ldub_phys(addr);
+        break;
+    case 1:
+        /* word access */
+        val = lduw_phys(addr);
+        break;
+    case 2:
+        /* tri-byte access; apparently possible with real pci lines.  */
+        qemu_log("pci: tri-byte mem read");
+        return ~0u;
+    default:
+        /* long access */
+        return ldl_phys(addr);
+    }
+
+    val <<= (addr & 3) * 8;
+    return val;
+}
+
+void alpha_sparse_mem_write(PCIBus *b, target_phys_addr_t addr, uint32_t val)
+{
+    int size = (addr >> 3) & 3;
+
+    addr = pci_to_cpu_addr(b, addr >> 5);
+    switch (size) {
+    case 0:
+        /* byte access */
+        val >>= (addr & 3) * 8;
+        stb_phys(addr, val);
+        break;
+    case 1:
+        /* word access */
+        val >>= (addr & 3) * 8;
+        stw_phys(addr, val);
+        break;
+    case 2:
+        /* tri-byte access; apparently possible with real pci lines.  */
+        qemu_log("pci: tri-byte mem write");
+        break;
+    default:
+        /* long access */
+        stl_phys(addr, val);
+        break;
+    }
+}
+
+uint32_t alpha_sparse_conf1_read(PCIBus *b, target_phys_addr_t addr)
+{
+    int size = ((addr >> 3) & 3) + 1;
+    uint32_t val;
+
+    if (size == 3) {
+        qemu_log("pci: tri-byte configuration read");
+        return ~0u;
+    }
+
+    addr >>= 5;
+    val = pci_data_read(b, addr, size);
+    val <<= (addr & 3) * 8;
+
+    return val;
+}
+
+void alpha_sparse_conf1_write(PCIBus *b, target_phys_addr_t addr, uint32_t val)
+{
+    int size = ((addr >> 3) & 3) + 1;
+
+    if (size == 3) {
+        qemu_log("pci: tri-byte configuration write");
+        return;
+    }
+
+    addr >>= 5;
+    val >>= (addr & 3) * 8;
+    pci_data_write(b, addr, val, size);
+}
+
+/* Configuration space accesses do not normall use an HAE.  */
+static uint32_t sparse_conf1_readl(void *opaque, target_phys_addr_t addr)
+{
+    PCIBus *b = opaque;
+    return alpha_sparse_conf1_read(b, addr);
+}
+
+static void sparse_conf1_writel(void *opaque, target_phys_addr_t addr,
+                                uint32_t val)
+{
+    PCIBus *b = opaque;
+    alpha_sparse_conf1_write(b, addr, val);
+}
+
+CPUReadMemoryFunc * const alpha_pci_sparse_conf1_reads[] = {
+    unassigned_mem_readb,
+    unassigned_mem_readw,
+    sparse_conf1_readl,
+};
+
+CPUWriteMemoryFunc * const alpha_pci_sparse_conf1_writes[] = {
+    unassigned_mem_writeb,
+    unassigned_mem_writew,
+    sparse_conf1_writel,
+};
+
+/* PCI/EISA Interrupt Acknowledge Cycle.  */
+
+static uint32_t iack_readl(void *opaque, target_phys_addr_t addr)
+{
+    if (addr & 15) {
+        return unassigned_mem_readl(opaque, addr);
+    }
+    return pic_read_irq(isa_pic);
+}
+
+static void special_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    qemu_log("pci: special write cycle %08x", val);
+}
+
+CPUReadMemoryFunc * const alpha_pci_iack_reads[] = {
+    unassigned_mem_readb,
+    unassigned_mem_readw,
+    iack_readl,
+};
+
+CPUWriteMemoryFunc * const alpha_pci_special_writes[] = {
+    unassigned_mem_writeb,
+    unassigned_mem_writew,
+    special_writel,
+};
diff --git a/hw/alpha_sys.h b/hw/alpha_sys.h
new file mode 100644
index 0000000..272a107
--- /dev/null
+++ b/hw/alpha_sys.h
@@ -0,0 +1,42 @@
+/* Alpha cores and system support chips.  */
+
+#ifndef HW_ALPHA_H
+#define HW_ALPHA_H 1
+
+#include "pci.h"
+#include "pci_host.h"
+#include "ide.h"
+#include "net.h"
+#include "pc.h"
+#include "usb-ohci.h"
+#include "irq.h"
+
+
+extern PCIBus *typhoon_init(qemu_irq *p_isa_irq, qemu_irq *p_rtc_irq,
+                            CPUState *cpus[3], pci_map_irq_fn sys_map_irq);
+
+/* alpha_pci.c.  */
+extern CPUReadMemoryFunc * const alpha_pci_bw_io_reads[];
+extern CPUWriteMemoryFunc * const alpha_pci_bw_io_writes[];
+extern CPUReadMemoryFunc * const alpha_pci_bw_conf1_reads[];
+extern CPUWriteMemoryFunc * const alpha_pci_bw_conf1_writes[];
+
+extern CPUReadMemoryFunc * const alpha_pci_dense_mem_reads[];
+extern CPUWriteMemoryFunc * const alpha_pci_dense_mem_writes[];
+
+extern CPUReadMemoryFunc * const alpha_pci_sparse_conf1_reads[];
+extern CPUWriteMemoryFunc * const alpha_pci_sparse_conf1_writes[];
+
+extern CPUReadMemoryFunc * const alpha_pci_iack_reads[];
+extern CPUWriteMemoryFunc * const alpha_pci_special_writes[];
+
+extern uint32_t alpha_sparse_io_read(target_phys_addr_t);
+extern void alpha_sparse_io_write(target_phys_addr_t, uint32_t);
+
+extern uint32_t alpha_sparse_mem_read(PCIBus *, target_phys_addr_t);
+extern void alpha_sparse_mem_write(PCIBus *, target_phys_addr_t, uint32_t);
+
+extern uint32_t alpha_sparse_conf1_read(PCIBus *, target_phys_addr_t);
+extern void alpha_sparse_conf1_write(PCIBus *, target_phys_addr_t, uint32_t);
+
+#endif
diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c
new file mode 100644
index 0000000..7cdf7d3
--- /dev/null
+++ b/hw/alpha_typhoon.c
@@ -0,0 +1,781 @@
+/*
+ * DEC 21272 (TSUNAMI/TYPHOON) chipset emulation.
+ *
+ * Written by Richard Henderson.
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ */
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "hw.h"
+#include "devices.h"
+#include "sysemu.h"
+#include "alpha_sys.h"
+
+typedef struct TyphoonCchip {
+    uint64_t misc;
+    uint64_t drir;
+    uint64_t dim[4];
+    uint32_t iic[4];
+    CPUState *cpu[4];
+} TyphoonCchip;
+
+typedef struct TyphoonWindow {
+    uint32_t base_addr;
+    uint32_t mask;
+    uint32_t translated_base_pfn;
+} TyphoonWindow;
+ 
+typedef struct TyphoonPchip {
+    uint64_t ctl;
+    TyphoonWindow win[4];
+} TyphoonPchip;
+
+typedef struct TyphoonState {
+    PCIHostState host;
+
+    TyphoonCchip cchip;
+    TyphoonPchip pchip;
+
+    /* QEMU emulation state.  */
+    uint32_t latch_tmp;
+} TyphoonState;
+
+/* Called when one of DRIR or DIM changes.  */
+static void cpu_irq_change(CPUState *env, uint64_t req)
+{
+    /* If there are any non-masked interrupts, tell the cpu.  */
+    if (env) {
+        if (req) {
+            cpu_interrupt(env, CPU_INTERRUPT_HARD);
+        } else {
+            cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+        }
+    }
+}
+
+static uint32_t cchip_readl(void *opaque, target_phys_addr_t addr)
+{
+    CPUState *env = cpu_single_env;
+    TyphoonState *s = opaque;
+    uint64_t ret = 0;
+
+    if (addr & 4) {
+        return s->latch_tmp;
+    }
+
+    switch (addr) {
+    case 0x0000:
+        /* CSC: Cchip System Configuration Register.  */
+        /* All sorts of data here; probably the only thing relevant is
+           PIP<14> Pchip 1 Present = 0.  */
+        break;
+
+    case 0x0040:
+        /* MTR: Memory Timing Register.  */
+        /* All sorts of stuff related to real DRAM.  */
+        break;
+
+    case 0x0080:
+        /* MISC: Miscellaneous Register.  */
+        ret = s->cchip.misc | (env->cpu_index & 3);
+        break;
+
+    case 0x00c0:
+        /* MPD: Memory Presence Detect Register.  */
+        break;
+
+    case 0x0100: /* AAR0 */
+    case 0x0140: /* AAR1 */
+    case 0x0180: /* AAR2 */
+    case 0x01c0: /* AAR3 */
+        /* AAR: Array Address Register.  */
+        /* All sorts of information about DRAM.  */
+        break;
+
+    case 0x0200:
+        /* DIM0: Device Interrupt Mask Register, CPU0.  */
+        ret = s->cchip.dim[0];
+        break;
+    case 0x0240:
+        /* DIM1: Device Interrupt Mask Register, CPU1.  */
+        ret = s->cchip.dim[1];
+        break;
+    case 0x0280:
+        /* DIR0: Device Interrupt Request Register, CPU0.  */
+        ret = s->cchip.dim[0] & s->cchip.drir;
+        break;
+    case 0x02c0:
+        /* DIR1: Device Interrupt Request Register, CPU1.  */
+        ret = s->cchip.dim[1] & s->cchip.drir;
+        break;
+    case 0x0300:
+        /* DRIR: Device Raw Interrupt Request Register.  */
+        ret = s->cchip.drir;
+        break;
+
+    case 0x0340:
+        /* PRBEN: Probe Enable Register.  */
+        break;
+
+    case 0x0380:
+        /* IIC0: Interval Ignore Count Register, CPU0.  */
+        ret = s->cchip.iic[0];
+        break;
+    case 0x03c0:
+        /* IIC1: Interval Ignore Count Register, CPU1.  */
+        ret = s->cchip.iic[1];
+        break;
+
+    case 0x0400: /* MPR0 */
+    case 0x0440: /* MPR1 */
+    case 0x0480: /* MPR2 */
+    case 0x04c0: /* MPR3 */
+        /* MPR: Memory Programming Register.  */
+        break;
+
+    case 0x0580:
+        /* TTR: TIGbus Timing Register.  */
+        /* All sorts of stuff related to interrupt delivery timings.  */
+        break;
+    case 0x05c0:
+        /* TDR: TIGbug Device Timing Register.  */
+        break;
+
+    case 0x0600:
+        /* DIM2: Device Interrupt Mask Register, CPU2.  */
+        ret = s->cchip.dim[2];
+        break;
+    case 0x0640:
+        /* DIM3: Device Interrupt Mask Register, CPU3.  */
+        ret = s->cchip.dim[3];
+        break;
+    case 0x0680:
+        /* DIR2: Device Interrupt Request Register, CPU2.  */
+        ret = s->cchip.dim[2] & s->cchip.drir;
+        break;
+    case 0x06c0:
+        /* DIR3: Device Interrupt Request Register, CPU3.  */
+        ret = s->cchip.dim[3] & s->cchip.drir;
+        break;
+
+    case 0x0700:
+        /* IIC2: Interval Ignore Count Register, CPU2.  */
+        ret = s->cchip.iic[2];
+        break;
+    case 0x0740:
+        /* IIC3: Interval Ignore Count Register, CPU3.  */
+        ret = s->cchip.iic[3];
+        break;
+
+    case 0x0780:
+        /* PWR: Power Management Control.   */
+        break;
+    
+    case 0x0c00: /* CMONCTLA */
+    case 0x0c40: /* CMONCTLB */
+    case 0x0c80: /* CMONCNT01 */
+    case 0x0cc0: /* CMONCNT23 */
+        break;
+
+    default:
+        do_unassigned_access(addr, 0, 0, 0, 4);
+        return -1;
+    }
+
+    s->latch_tmp = ret >> 32;
+    return ret;
+}
+
+static uint32_t dchip_readl(void *opaque, target_phys_addr_t addr)
+{
+    /* Skip this.  It's all related to DRAM timing and setup.  */
+    return 0;
+}
+
+static uint32_t pchip_readl(void *opaque, target_phys_addr_t addr)
+{
+    TyphoonState *s = opaque;
+    uint64_t ret = 0;
+
+    if (addr & 4) {
+        return s->latch_tmp;
+    }
+
+    switch (addr) {
+    case 0x0000:
+        /* WSBA0: Window Space Base Address Register.  */
+        ret = s->pchip.win[0].base_addr;
+        break;
+    case 0x0040:
+        /* WSBA1 */
+        ret = s->pchip.win[1].base_addr;
+        break;
+    case 0x0080:
+        /* WSBA2 */
+        ret = s->pchip.win[2].base_addr;
+        break;
+    case 0x00c0:
+        /* WSBA3 */
+        ret = s->pchip.win[3].base_addr;
+        break;
+
+    case 0x0100:
+        /* WSM0: Window Space Mask Register.  */
+        ret = s->pchip.win[0].mask;
+        break;
+    case 0x0140:
+        /* WSM1 */
+        ret = s->pchip.win[1].mask;
+        break;
+    case 0x0180:
+        /* WSM2 */
+        ret = s->pchip.win[2].mask;
+        break;
+    case 0x01c0:
+        /* WSM3 */
+        ret = s->pchip.win[3].mask;
+        break;
+
+    case 0x0200:
+        /* TBA0: Translated Base Address Register.  */
+        ret = (uint64_t)s->pchip.win[0].translated_base_pfn << 10;
+        break;
+    case 0x0240:
+        /* TBA1 */
+        ret = (uint64_t)s->pchip.win[1].translated_base_pfn << 10;
+        break;
+    case 0x0280:
+        /* TBA2 */
+        ret = (uint64_t)s->pchip.win[2].translated_base_pfn << 10;
+        break;
+    case 0x02c0:
+        /* TBA3 */
+        ret = (uint64_t)s->pchip.win[3].translated_base_pfn << 10;
+        break;
+
+    case 0x0300:
+        /* PCTL: Pchip Control Register.  */
+        ret = s->pchip.ctl;
+        break;
+    case 0x0340:
+        /* PLAT: Pchip Master Latency Register.  */
+        break;
+    case 0x03c0:
+        /* PERROR: Pchip Error Register.  */
+        break;
+    case 0x0400:
+        /* PERRMASK: Pchip Error Mask Register.  */
+        break;
+    case 0x0440:
+        /* PERRSET: Pchip Error Set Register.  */
+        break;
+    case 0x0480:
+        /* TLBIV: Translation Buffer Invalidate Virtual Register (WO).  */
+        break;
+    case 0x04c0:
+        /* TLBIA: Translation Buffer Invalidate All Register (WO).  */
+        break;
+    case 0x0500: /* PMONCTL */
+    case 0x0540: /* PMONCNT */
+    case 0x0800: /* SPRST */
+        break;
+
+    default:
+        do_unassigned_access(addr, 0, 0, 0, 4);
+        return -1;
+    }
+
+    s->latch_tmp = ret >> 32;
+    return ret;
+}
+
+static void cchip_writel(void *opaque, target_phys_addr_t addr, uint32_t v32)
+{
+    TyphoonState *s = opaque;
+    uint64_t val, oldval, newval;
+
+    if (addr & 4) {
+        val = (uint64_t)v32 << 32 | s->latch_tmp;
+        addr ^= 4;
+    } else {
+        s->latch_tmp = v32;
+        return;
+    }
+
+    switch (addr) {
+    case 0x0000:
+        /* CSC: Cchip System Configuration Register.  */
+        /* All sorts of data here; nothing relevant RW.  */
+        break;
+
+    case 0x0040:
+        /* MTR: Memory Timing Register.  */
+        /* All sorts of stuff related to real DRAM.  */
+        break;
+
+    case 0x0080:
+        /* MISC: Miscellaneous Register.  */
+        newval = oldval = s->cchip.misc;
+        newval &= ~(val & 0x10000ff0);     /* W1C fields */
+        if (val & 0x100000) {
+            newval &= ~0xff0000ull;        /* ACL clears ABT and ABW */
+        } else {
+            newval |= val & 0x00f00000;    /* ABT field is W1S */
+            if ((newval & 0xf0000) == 0) {
+                newval |= val & 0xf0000;   /* ABW field is W1S iff zero */
+            }
+        }
+        newval |= (val & 0xf000) >> 4;     /* IPREQ field sets IPINTR.  */
+
+        newval &= ~0xf0000000000ull;       /* WO and RW fields */
+        newval |= val & 0xf0000000000ull;
+        s->cchip.misc = newval;
+
+        /* Pass on changes to IPI and ITI state.  */
+        if ((newval ^ oldval) & 0xff0) {
+            int i;
+            for (i = 0; i < 4; ++i) {
+                CPUState *env = s->cchip.cpu[i];
+                if (env) {
+                    /* IPI can be either cleared or set by the write.  */
+                    if (newval & (1 << (i + 8))) {
+                        cpu_interrupt(env, CPU_INTERRUPT_SMP);
+                    } else {
+                        cpu_reset_interrupt(env, CPU_INTERRUPT_SMP);
+                    }
+
+                    /* ITI can only be cleared by the write.  */
+                    if ((newval & (1 << (i + 4))) == 0) {
+                        cpu_reset_interrupt(env, CPU_INTERRUPT_TIMER);
+                    }
+                }
+            }
+        }
+        break;
+
+    case 0x00c0:
+        /* MPD: Memory Presence Detect Register.  */
+        break;
+
+    case 0x0100: /* AAR0 */
+    case 0x0140: /* AAR1 */
+    case 0x0180: /* AAR2 */
+    case 0x01c0: /* AAR3 */
+        /* AAR: Array Address Register.  */
+        /* All sorts of information about DRAM.  */
+        break;
+
+    case 0x0200: /* DIM0 */
+        /* DIM: Device Interrupt Mask Register, CPU0.  */
+        s->cchip.dim[0] = val;
+        cpu_irq_change(s->cchip.cpu[0], val & s->cchip.drir);
+        break;
+    case 0x0240: /* DIM1 */
+        /* DIM: Device Interrupt Mask Register, CPU1.  */
+        s->cchip.dim[0] = val;
+        cpu_irq_change(s->cchip.cpu[1], val & s->cchip.drir);
+        break;
+
+    case 0x0280: /* DIR0 (RO) */
+    case 0x02c0: /* DIR1 (RO) */
+    case 0x0300: /* DRIR (RO) */
+        break;
+
+    case 0x0340:
+        /* PRBEN: Probe Enable Register.  */
+        break;
+
+    case 0x0380: /* IIC0 */
+        s->cchip.iic[0] = val & 0xffffff;
+        break;
+    case 0x03c0: /* IIC1 */
+        s->cchip.iic[1] = val & 0xffffff;
+        break;
+
+    case 0x0400: /* MPR0 */
+    case 0x0440: /* MPR1 */
+    case 0x0480: /* MPR2 */
+    case 0x04c0: /* MPR3 */
+        /* MPR: Memory Programming Register.  */
+        break;
+
+    case 0x0580:
+        /* TTR: TIGbus Timing Register.  */
+        /* All sorts of stuff related to interrupt delivery timings.  */
+        break;
+    case 0x05c0:
+        /* TDR: TIGbug Device Timing Register.  */
+        break;
+
+    case 0x0600:
+        /* DIM2: Device Interrupt Mask Register, CPU2.  */
+        s->cchip.dim[2] = val;
+        cpu_irq_change(s->cchip.cpu[2], val & s->cchip.drir);
+        break;
+    case 0x0640:
+        /* DIM3: Device Interrupt Mask Register, CPU3.  */
+        s->cchip.dim[3] = val;
+        cpu_irq_change(s->cchip.cpu[3], val & s->cchip.drir);
+        break;
+
+    case 0x0680: /* DIR2 (RO) */
+    case 0x06c0: /* DIR3 (RO) */
+        break;
+
+    case 0x0700: /* IIC2 */
+        s->cchip.iic[2] = val & 0xffffff;
+        break;
+    case 0x0740: /* IIC3 */
+        s->cchip.iic[3] = val & 0xffffff;
+        break;
+
+    case 0x0780:
+        /* PWR: Power Management Control.   */
+        break;
+    
+    case 0x0c00: /* CMONCTLA */
+    case 0x0c40: /* CMONCTLB */
+    case 0x0c80: /* CMONCNT01 */
+    case 0x0cc0: /* CMONCNT23 */
+        break;
+
+    default:
+        do_unassigned_access(addr, 1, 0, 0, 4);
+        return;
+    }
+}
+
+static void dchip_writel(void *opaque, target_phys_addr_t addr, uint32_t v32)
+{
+    /* Skip this.  It's all related to DRAM timing and setup.  */
+}
+
+static void pchip_writel(void *opaque, target_phys_addr_t addr, uint32_t v32)
+{
+    TyphoonState *s = opaque;
+    uint64_t val, oldval;
+
+    switch (addr) {
+    case 0x0000:
+        /* WSBA0: Window Space Base Address Register.  */
+        s->pchip.win[0].base_addr = v32;
+        break;
+    case 0x0040:
+        /* WSBA1 */
+        s->pchip.win[1].base_addr = v32;
+        break;
+    case 0x0080:
+        /* WSBA2 */
+        s->pchip.win[2].base_addr = v32;
+        break;
+    case 0x00c0:
+        /* WSBA3 */
+        s->pchip.win[3].base_addr = v32;
+        break;
+
+    case 0x0100:
+        /* WSM0: Window Space Mask Register.  */
+        s->pchip.win[0].mask = v32;
+        break;
+    case 0x0140:
+        /* WSM1 */
+        s->pchip.win[1].mask = v32;
+        break;
+    case 0x0180:
+        /* WSM2 */
+        s->pchip.win[2].mask = v32;
+        break;
+    case 0x01c0:
+        /* WSM3 */
+        s->pchip.win[3].mask = v32;
+        break;
+
+    case 0x0200:
+        /* TBA0: Translated Base Address Register.  */
+        s->pchip.win[0].translated_base_pfn = v32 >> 10;
+        break;
+    case 0x0240:
+        /* TBA1 */
+        s->pchip.win[1].translated_base_pfn = v32 >> 10;
+        break;
+    case 0x0280:
+        /* TBA2 */
+        s->pchip.win[2].translated_base_pfn = v32 >> 10;
+        break;
+    case 0x02c0:
+        /* TBA3 */
+        s->pchip.win[3].translated_base_pfn = v32 >> 10;
+        break;
+
+    case 0x0004:
+    case 0x0044:
+    case 0x0084:
+    case 0x00c4:
+    case 0x0104:
+    case 0x0144:
+    case 0x0184:
+    case 0x01c4:
+    case 0x0204:
+    case 0x0244:
+    case 0x0284:
+    case 0x02c4:
+        /* MBZ high-part portions of the above registers.  */
+        break;
+
+    case 0x0300:
+        /* PCTL: Pchip Control Register.  */
+        s->latch_tmp = v32;
+        break;
+    case 0x0304:
+        val = (uint64_t)v32 << 32 | s->latch_tmp;
+
+        oldval = s->pchip.ctl;
+        oldval &= ~0x00001cff0fc7ffull;       /* RW fields */
+        oldval |= val & 0x00001cff0fc7ffull;
+
+        s->pchip.ctl = oldval;
+        break;
+
+    case 0x0340: case 0x0344:
+        /* PLAT: Pchip Master Latency Register.  */
+        break;
+    case 0x03c0: case 0x03c4:
+        /* PERROR: Pchip Error Register.  */
+        break;
+    case 0x0400: case 0x0404:
+        /* PERRMASK: Pchip Error Mask Register.  */
+        break;
+    case 0x0440: case 0x0444:
+        /* PERRSET: Pchip Error Set Register.  */
+        break;
+
+    case 0x0480:
+        /* TLBIV: Translation Buffer Invalidate Virtual Register.  */
+        s->latch_tmp = v32;
+        break;
+    case 0x0484:
+        val = (uint64_t)v32 << 32 | s->latch_tmp;
+        break;
+
+    case 0x04c0:
+        /* TLBIA: Translation Buffer Invalidate All Register (WO).  */
+        break;
+    case 0x04c4:
+        break;
+
+    case 0x0500: case 0x0504:
+        /* PMONCTL */
+    case 0x0540: case 0x0544:
+        /* PMONCNT */
+    case 0x0800: case 0x0804:
+        /* SPRST */
+        break;
+
+    default:
+        do_unassigned_access(addr, 1, 0, 0, 4);
+        return;
+    }
+}
+
+static CPUReadMemoryFunc * const cchip_reads[] = {
+    NULL,
+    NULL,
+    cchip_readl
+};
+
+static CPUWriteMemoryFunc * const cchip_writes[] = {
+    NULL,
+    NULL,
+    cchip_writel
+};
+
+static CPUReadMemoryFunc * const dchip_reads[] = {
+    NULL,
+    NULL,
+    dchip_readl
+};
+
+static CPUWriteMemoryFunc * const dchip_writes[] = {
+    NULL,
+    NULL,
+    dchip_writel
+};
+
+static CPUReadMemoryFunc * const pchip_reads[] = {
+    NULL,
+    NULL,
+    pchip_readl 
+};
+
+static CPUWriteMemoryFunc * const pchip_writes[] = {
+    NULL,
+    NULL,
+    pchip_writel
+};
+
+static void typhoon_set_irq(void *opaque, int irq, int level)
+{
+    TyphoonState *s = opaque;
+    uint64_t drir;
+    int i;
+
+    /* Set/Reset the bit in CCHIP.DRIR based on IRQ+LEVEL.  */
+    drir = s->cchip.drir;
+    if (level) {
+        drir |= 1ull << irq;
+    } else {
+        drir &= ~(1ull << irq);
+    }
+    s->cchip.drir = drir;
+
+    for (i = 0; i < 4; ++i) {
+        cpu_irq_change(s->cchip.cpu[i], s->cchip.dim[i] & drir);
+    }
+}
+
+static void typhoon_set_isa_irq(void *opaque, int irq, int level)
+{
+    typhoon_set_irq(opaque, 55, level);
+}
+
+static void typhoon_set_timer_irq(void *opaque, int irq, int level)
+{
+    TyphoonState *s = opaque;
+    int i;
+
+    /* Thankfully, the mc146818rtc code doesn't track the IRQ state,
+       and so we don't have to worry about missing interrupts just
+       because we never actually ACK the interrupt.  Just ignore any
+       case of the interrupt level going low.  */
+    if (level == 0) {
+        return;
+    }
+
+    /* Deliver the interrupt to each CPU, considering each CPU's IIC.  */
+    for (i = 0; i < 4; ++i) {
+        CPUState *env = s->cchip.cpu[i];
+        if (env) {
+            uint32_t iic = s->cchip.iic[i];
+
+            /* ??? The verbage in Section 10.2.2.10 isn't 100% clear.
+               Bit 24 is the OverFlow bit, RO, and set when the count
+               decrements past 0.  When is OF cleared?  My guess is that
+               OF is actually cleared when the IIC is written, and that
+               the ICNT field always decrements.  At least, that's an
+               interpretation that makes sense, and "allows the CPU to
+               determine exactly how mant interval timer ticks were
+               skipped".  At least within the next 4M ticks...  */
+
+            iic = ((iic - 1) & 0x1ffffff) | (iic & 0x1000000);
+            s->cchip.iic[i] = iic;
+
+            if (iic & 0x1000000) {
+                /* Set the ITI bit for this cpu.  */
+                s->cchip.misc |= 1 << (i + 4);
+                /* And signal the interrupt.  */
+                cpu_interrupt(env, CPU_INTERRUPT_TIMER);
+            }
+        }
+    }
+}
+
+PCIBus *typhoon_init(qemu_irq *p_isa_irq, qemu_irq *p_rtc_irq,
+                     CPUState *cpus[3], pci_map_irq_fn sys_map_irq)
+{
+    const uint64_t MB = 1024 * 1024;
+    DeviceState *dev;
+    PCIHostState *p;
+    TyphoonState *s;
+    PCIBus *b;
+    int region;
+
+    dev = qdev_create(NULL, "typhoon-pcihost");
+    p = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev));
+    s = container_of(p, TyphoonState, host);
+
+    /* Remember the CPUs so that we can deliver interrupts to them.  */
+    memcpy(s->cchip.cpu, cpus, 4 * sizeof(CPUState *));
+
+    *p_isa_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1);
+    *p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1);
+
+    b = pci_register_bus(&s->host.busdev.qdev, "pci", typhoon_set_irq,
+                         sys_map_irq, s, 0, 64);
+    s->host.bus = b;
+
+    qdev_init_nofail(dev);
+
+    /* Main memory region, 0x00.0000.0000.  Real hardware supports 32GB,
+       but the address space hole reserved at this point is 8TB.  */
+
+    /* Pchip0 PCI memory, 0x800.0000.0000, 4GB.  */
+    pci_bus_set_mem_base(b, 0x80000000000ull);
+
+    /* TIGbus, 0x801.0000.0000, 1GB.  */
+    /* ??? The TIGbus is used for delivering interrupts, and access to
+       the flash ROM.  I'm not sure that we need to implement it at all.  */
+
+    /* Pchip0 CSRs, 0x801.8000.0000, 256MB.  */
+    region = cpu_register_io_memory(pchip_reads, pchip_writes, s,
+                                    DEVICE_LITTLE_ENDIAN);
+    cpu_register_physical_memory(0x80180000000, 256*MB, region);
+
+    /* Cchip CSRs, 0x801.A000.0000, 256MB.  */
+    region = cpu_register_io_memory(cchip_reads, cchip_writes, s,
+                                    DEVICE_LITTLE_ENDIAN);
+    cpu_register_physical_memory(0x801a0000000, 256*MB, region);
+
+    /* Dchip CSRs, 0x801.B000.0000, 256MB.  */
+    region = cpu_register_io_memory(dchip_reads, dchip_writes, s,
+                                    DEVICE_LITTLE_ENDIAN);
+    cpu_register_physical_memory(0x801b0000000, 256*MB, region);
+
+    /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB.  */
+    region = cpu_register_io_memory(alpha_pci_iack_reads,
+                                    alpha_pci_special_writes, b,
+                                    DEVICE_LITTLE_ENDIAN);
+    cpu_register_physical_memory(0x801f8000000, 64*MB, region);
+
+    /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB.  */
+    region = cpu_register_io_memory(alpha_pci_bw_io_reads,
+                                    alpha_pci_bw_io_writes, b,
+                                    DEVICE_LITTLE_ENDIAN);
+    cpu_register_physical_memory(0x801fc000000, 32*MB, region);
+
+    /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB.  */
+    region = cpu_register_io_memory(alpha_pci_bw_conf1_reads,
+                                    alpha_pci_bw_conf1_writes, b,
+                                    DEVICE_LITTLE_ENDIAN);
+    cpu_register_physical_memory(0x801fe000000, 16*MB, region);
+
+    /* For the record, these are the mappings for the second PCI bus.
+       We can get away with not implementing them because we indicate
+       via the Cchip.CSC<PIP> bit that Pchip1 is not present.  */
+    /* Pchip1 PCI memory, 0x802.0000.0000, 4GB.  */
+    /* Pchip1 CSRs, 0x802.8000.0000, 256MB.  */
+    /* Pchip1 PCI special/interrupt acknowledge, 0x802.F800.0000, 64MB.  */
+    /* Pchip1 PCI I/O, 0x802.FC00.0000, 32MB.  */
+    /* Pchip1 PCI configuration, 0x802.FE00.0000, 16MB.  */
+
+    return b;
+}
+
+static int typhoon_pcihost_init(SysBusDevice *dev)
+{
+    return 0;
+}
+
+static SysBusDeviceInfo typhoon_pcihost_info = {
+    .init = typhoon_pcihost_init,
+    .qdev.name = "typhoon-pcihost",
+    .qdev.size = sizeof(TyphoonState),
+    .qdev.no_user = 1
+};
+
+static void typhoon_register(void)
+{
+    sysbus_register_withprop(&typhoon_pcihost_info);
+}
+device_init(typhoon_register);
