diff mbox

[V4,06/18] support NEC PC-9821 memory

Message ID 200912221740.AA00209@YOUR-BD18D6DD63.m1.interq.or.jp
State New
Headers show

Commit Message

武田 =?ISO-2022-JP?B?IBskQj1TTGkbKEI=?= Dec. 22, 2009, 5:40 p.m. UTC
Signed-off-by: TAKEDA, toshiya <t-takeda@m1.interq.or.jp>
---
 hw/pc98mem.c |  861 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 861 insertions(+), 0 deletions(-)
 create mode 100644 hw/pc98mem.c
diff mbox

Patch

diff --git a/hw/pc98mem.c b/hw/pc98mem.c
new file mode 100644
index 0000000..36a60cc
--- /dev/null
+++ b/hw/pc98mem.c
@@ -0,0 +1,861 @@ 
+/*
+ * QEMU NEC PC-9821 memory
+ *
+ * Copyright (c) 2009 TAKEDA, toshiya
+ *
+ * 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 "pc.h"
+#include "isa.h"
+#include "loader.h"
+
+#define PCI_FILE_NAME   "pc98pci.bin"
+#define PCI_FILE_SIZE   0x8000
+#define IDE_FILE_NAME   "pc98ide.bin"
+#define IDE_FILE_SIZE   0x2000
+#define ITF_FILE_NAME   "pc98itf.bin"
+#define ITF_FILE_SIZE   0x8000
+#define BIOS_FILE_NAME  "pc98bios.bin"
+#define BIOS_FILE_SIZE  0x18000
+#define BANK_FILE_NAME  "pc98bank%d.bin"
+#define BANK_FILE_SIZE  0x8000
+
+#define PCI_ROM_BANK  0
+#define IDE_ROM_BANK  3
+#define ITF_ROM_BANK  4
+#define BIOS_ROM_BANK 5
+#define ROM_BANK_NUM  8
+
+#define REQUIRED_ROM_BANK ((1 << ITF_ROM_BANK) | (7 << BIOS_ROM_BANK))
+
+#define BANK_BITS 12
+/* (1 << BANK_BITS) */
+#define BANK_SIZE 0x1000
+
+#define PCI_BIOS_OFS   (BANK_FILE_SIZE * PCI_ROM_BANK)
+#define IDE_BIOS_OFS   (BANK_FILE_SIZE * IDE_ROM_BANK)
+#define ITF_OFS        (BANK_FILE_SIZE * ITF_ROM_BANK)
+#define BIOS_OFS       (BANK_FILE_SIZE * BIOS_ROM_BANK)
+#define NONE_OFS       (BANK_FILE_SIZE * ROM_BANK_NUM)
+#define TOTAL_ROM_SIZE (NONE_OFS + BANK_SIZE)
+
+enum {
+    D8000_BANK_IDE = 1,
+    D8000_BANK_PCI = 2,
+    D8000_BANK_PnP = 3,
+};
+
+struct MemoryState {
+    ram_addr_t ram_addr[0x1000000 >> BANK_BITS]; /* 16MB */
+    ram_addr_t rom_addr[TOTAL_ROM_SIZE >> BANK_BITS];
+    ram_addr_t mem_bank[0x100000 >> BANK_BITS]; /* 1MB */
+
+    int tvram_io_memory;
+    int vram_a8000_io_memory;
+    int vram_b0000_io_memory;
+    int vram_e0000_io_memory;
+    int vram_f00000_io_memory;
+
+    uint8_t ram_window_map1;
+    uint8_t ram_window_map2;
+    uint8_t d8000_bank;
+    uint8_t ide_bios_enabled;
+    uint8_t ide_ram_selected;
+    uint8_t bios_ram_selected;
+    uint8_t itf_selected;
+    uint8_t use_system_16mb;
+
+    uint8_t ide_bios_loaded;
+    uint8_t hd_connect;
+    uint8_t init_done;
+};
+
+typedef struct MemoryState MemoryState;
+
+static uint8_t *get_ram_ptr(void *opaque, ram_addr_t addr)
+{
+    MemoryState *s = opaque;
+    uint32_t ofs = addr & (BANK_SIZE - 1);
+
+    return qemu_get_ram_ptr(s->ram_addr[addr >> BANK_BITS]) + ofs;
+}
+
+static void register_ram(void *opaque, target_phys_addr_t top, ram_addr_t size,
+                         ram_addr_t addr)
+{
+    MemoryState *s = opaque;
+    target_phys_addr_t a;
+    int smram_update = 0;
+
+    for (a = top; a < top + size; a += BANK_SIZE) {
+        ram_addr_t phys_offset = s->ram_addr[addr >> BANK_BITS];
+        if (s->mem_bank[a >> BANK_BITS] != phys_offset) {
+            cpu_register_physical_memory(a, BANK_SIZE, phys_offset);
+            if (a >= 0xa0000) {
+                smram_update = 1;
+                if (s->use_system_16mb) {
+                    cpu_register_physical_memory(0xf00000 + a, BANK_SIZE,
+                                                 phys_offset);
+                }
+                cpu_register_physical_memory(0xfff00000 + a, BANK_SIZE,
+                                             phys_offset);
+            }
+            s->mem_bank[a >> BANK_BITS] = phys_offset;
+        }
+        addr += BANK_SIZE;
+    }
+    if (i440fx_state && smram_update && s->init_done) {
+        i440fx_init_memory_mappings(i440fx_state);
+    }
+}
+
+static void register_rom(void *opaque, target_phys_addr_t top, ram_addr_t size,
+                         ram_addr_t addr)
+{
+    MemoryState *s = opaque;
+    target_phys_addr_t a;
+    int smram_update = 0;
+
+    for (a = top; a < top + size; a += BANK_SIZE) {
+        ram_addr_t phys_offset = s->rom_addr[addr >> BANK_BITS];
+        if (s->mem_bank[a >> BANK_BITS] != phys_offset) {
+            cpu_register_physical_memory(a, BANK_SIZE,
+                                         phys_offset | IO_MEM_ROM);
+            if (a >= 0xa0000) {
+                smram_update = 1;
+                if (s->use_system_16mb) {
+                    cpu_register_physical_memory(0xf00000 + a, BANK_SIZE,
+                                                 phys_offset | IO_MEM_ROM);
+                }
+                cpu_register_physical_memory(0xfff00000 + a, BANK_SIZE,
+                                             phys_offset | IO_MEM_ROM);
+            }
+            s->mem_bank[a >> BANK_BITS] = phys_offset;
+        }
+        if (addr != NONE_OFS) {
+            addr += BANK_SIZE;
+        }
+    }
+    if (i440fx_state && smram_update && s->init_done) {
+        i440fx_init_memory_mappings(i440fx_state);
+    }
+}
+
+static void register_io_memory(void *opaque, target_phys_addr_t top, ram_addr_t size,
+                               ram_addr_t phys_offset)
+{
+    MemoryState *s = opaque;
+    target_phys_addr_t a;
+    int smram_update = 0;
+
+    if (s->mem_bank[top >> BANK_BITS] != phys_offset) {
+        cpu_register_physical_memory(isa_mem_base + top, size, phys_offset);
+        if (top >= 0xa0000) {
+            smram_update = 1;
+            if (s->use_system_16mb) {
+                cpu_register_physical_memory(isa_mem_base + 0xf00000 + top,
+                                             size, phys_offset);
+            }
+            cpu_register_physical_memory(isa_mem_base + 0xfff00000 + top,
+                                         size, phys_offset);
+        }
+        for (a = top + BANK_SIZE; a < top + size; a += BANK_SIZE) {
+            s->mem_bank[a >> BANK_BITS] = -1;
+        }
+        s->mem_bank[top >> BANK_BITS] = phys_offset;
+    }
+    if (i440fx_state && smram_update && s->init_done) {
+        i440fx_init_memory_mappings(i440fx_state);
+    }
+}
+
+static void register_ide_rom(void *opaque)
+{
+    MemoryState *s = opaque;
+
+    if (!s->ide_bios_enabled) {
+        register_rom(s, 0xd8000, 0x8000, NONE_OFS);
+    } else if (s->ide_ram_selected) {
+        register_rom(s, 0xd8000, 0x2000, IDE_BIOS_OFS);
+        register_ram(s, 0xda000, 0x2000, 0xda000);
+        register_rom(s, 0xdc000, 0x4000, NONE_OFS);
+    } else {
+        register_rom(s, 0xd8000, 0x2000, IDE_BIOS_OFS);
+        register_rom(s, 0xda000, 0x6000, NONE_OFS);
+    }
+}
+
+static void register_pci_rom(void *opaque)
+{
+    register_rom(opaque, 0xd8000, 0x8000, PCI_BIOS_OFS);
+}
+
+static void register_pnp_rom(void *opaque)
+{
+    /* XXX: register plug&play bios at 0xd8000-0xdffff */
+}
+
+static void register_bios_rom(void *opaque)
+{
+    register_rom(opaque, 0xe8000, 0x18000, BIOS_OFS);
+}
+
+static void register_bios_ram(void *opaque)
+{
+    register_ram(opaque, 0xe8000, 0x18000, 0xe8000);
+}
+
+static void register_itf_rom(void *opaque)
+{
+    register_rom(opaque, 0xe8000, 0x10000, NONE_OFS);
+    register_rom(opaque, 0xf8000, 0x08000, ITF_OFS);
+}
+
+static void ioport_43b_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    MemoryState *s = opaque;
+    target_phys_addr_t a;
+
+    if (data & 0x04) {
+        if (s->use_system_16mb) {
+            s->use_system_16mb = 0;
+            qemu_unregister_coalesced_mmio(isa_mem_base + 0xf00000, 0xc0000);
+            qemu_unregister_coalesced_mmio(isa_mem_base + 0xfe0000, 0x08000);
+            for (a = 0xf00000; a < 0x1000000; a += BANK_SIZE) {
+                cpu_register_physical_memory(a, BANK_SIZE,
+                                             s->ram_addr[a >> BANK_BITS]);
+            }
+        }
+    } else {
+        if (!s->use_system_16mb) {
+            s->use_system_16mb = 1;
+            cpu_register_physical_memory(isa_mem_base + 0xf00000, 0xa0000,
+                                         s->vram_f00000_io_memory);
+            cpu_register_physical_memory(isa_mem_base + 0xfa0000, 0x08000,
+                                         s->tvram_io_memory);
+            cpu_register_physical_memory(isa_mem_base + 0xfa8000, 0x08000,
+                                         s->vram_a8000_io_memory);
+            cpu_register_physical_memory(isa_mem_base + 0xfb0000, 0x10000,
+                                         s->vram_b0000_io_memory);
+            cpu_register_physical_memory(isa_mem_base + 0xfe0000, 0x08000,
+                                         s->vram_e0000_io_memory);
+            for (a = 0xfc0000; a < 0xfe0000; a += BANK_SIZE) {
+                ram_addr_t phys_offset = cpu_get_physical_page_desc(a & 0xfffff);
+                cpu_register_physical_memory(a, BANK_SIZE, phys_offset);
+            }
+            for (a = 0xfe8000; a < 0x1000000; a += BANK_SIZE) {
+                ram_addr_t phys_offset = cpu_get_physical_page_desc(a & 0xfffff);
+                cpu_register_physical_memory(a, BANK_SIZE, phys_offset);
+            }
+            qemu_register_coalesced_mmio(isa_mem_base + 0xf00000, 0xc0000);
+            qemu_register_coalesced_mmio(isa_mem_base + 0xfe0000, 0x08000);
+        }
+    }
+}
+
+static uint32_t ioport_43b_read(void *opaque, uint32_t addr)
+{
+    MemoryState *s = opaque;
+
+    if (s->use_system_16mb) {
+        return 0x00;
+    } else {
+        return 0x04;
+    }
+}
+
+static void ioport_43d_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    MemoryState *s = opaque;
+
+    switch (data) {
+    case 0x00:
+    case 0x10:
+    case 0x18:
+        if (!s->itf_selected) {
+            s->itf_selected = 1;
+            register_itf_rom(s);
+        }
+        break;
+    case 0x02:
+    case 0x12:
+        if (s->itf_selected) {
+            s->itf_selected = 0;
+            if (s->bios_ram_selected) {
+                if (s->ide_bios_loaded && s->hd_connect) {
+                    /* IDE BIOS patch */
+                    if (s->hd_connect & 1) {
+                        *get_ram_ptr(s, 0x457) = 0x90;
+                        *get_ram_ptr(s, 0x45d) |= 0x08;
+                        *get_ram_ptr(s, 0x55d) |= 0x01;
+                    } else {
+                        *get_ram_ptr(s, 0x457) = 0x38;
+                    }
+                    if (s->hd_connect & 2) {
+                        *get_ram_ptr(s, 0x457) |= 0x42;
+                        *get_ram_ptr(s, 0x45d) |= 0x10;
+                        *get_ram_ptr(s, 0x55d) |= 0x02;
+                    } else {
+                        *get_ram_ptr(s, 0x457) |= 0x07;
+                    }
+                    *get_ram_ptr(s, 0xf8e90) |= (s->hd_connect & 0x0f);
+                }
+                register_bios_ram(s);
+            } else {
+                register_bios_rom(s);
+            }
+        }
+        break;
+    }
+}
+
+static void ioport_461_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    MemoryState *s = opaque;
+
+    if (s->ram_window_map1 != (data & 0xfe)) {
+        if (s->ram_window_map1 == 0x0a) {
+            qemu_unregister_coalesced_mmio(isa_mem_base + 0x80000, 0x20000);
+        } else if (s->ram_window_map1 == 0x0e) {
+            qemu_unregister_coalesced_mmio(isa_mem_base + 0x80000, 0x08000);
+        }
+        s->ram_window_map1 = data & 0xfe;
+        if (s->ram_window_map1 == 0x0a) {
+            register_io_memory(s, 0x80000, 0x08000, s->tvram_io_memory);
+            register_io_memory(s, 0x88000, 0x08000, s->vram_a8000_io_memory);
+            register_io_memory(s, 0x90000, 0x10000, s->vram_b0000_io_memory);
+            qemu_register_coalesced_mmio(isa_mem_base + 0x80000, 0x20000);
+        } else if (s->ram_window_map1 == 0x0e) {
+            register_io_memory(s, 0x80000, 0x08000, s->vram_e0000_io_memory);
+            qemu_register_coalesced_mmio(isa_mem_base + 0x80000, 0x08000);
+            register_ram(s, 0x88000, 0x18000, 0xe8000);
+        } else {
+            register_ram(s, 0x80000, 0x20000, s->ram_window_map1 * 0x10000);
+        }
+    }
+}
+
+static uint32_t ioport_461_read(void *opaque, uint32_t addr)
+{
+    MemoryState *s = opaque;
+
+    return s->ram_window_map1;
+}
+
+static void ioport_463_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    MemoryState *s = opaque;
+
+    if (s->ram_window_map2 != (data & 0xfe)) {
+        if (s->ram_window_map2 == 0x0a) {
+            qemu_unregister_coalesced_mmio(isa_mem_base + 0xa0000, 0x20000);
+        } else if (s->ram_window_map2 == 0x0e) {
+            qemu_unregister_coalesced_mmio(isa_mem_base + 0xa0000, 0x08000);
+        }
+        s->ram_window_map2 = data & 0xfe;
+        if (s->ram_window_map2 == 0x0a) {
+            register_io_memory(s, 0xa0000, 0x08000, s->tvram_io_memory);
+            register_io_memory(s, 0xa8000, 0x08000, s->vram_a8000_io_memory);
+            register_io_memory(s, 0xb0000, 0x10000, s->vram_b0000_io_memory);
+            qemu_register_coalesced_mmio(isa_mem_base + 0xa0000, 0x20000);
+        } else if (s->ram_window_map2 == 0x0e) {
+            register_io_memory(s, 0xa0000, 0x08000, s->vram_e0000_io_memory);
+            qemu_register_coalesced_mmio(isa_mem_base + 0xa0000, 0x08000);
+            register_ram(s, 0xa8000, 0x18000, 0xe8000);
+        } else {
+            register_ram(s, 0xa0000, 0x20000, s->ram_window_map2 * 0x10000);
+        }
+    }
+}
+
+static uint32_t ioport_463_read(void *opaque, uint32_t addr)
+{
+    MemoryState *s = opaque;
+
+    return s->ram_window_map2;
+}
+
+static void ioport_53d_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    MemoryState *s = opaque;
+
+    if (data & 0x10) {
+        if (!s->ide_bios_enabled) {
+            s->ide_bios_enabled = 1;
+            register_ide_rom(s);
+        }
+    } else {
+        if (s->ide_bios_enabled) {
+            s->ide_bios_enabled = 0;
+            register_ide_rom(s);
+        }
+    }
+    if (data & 0x02) {
+        if (!s->bios_ram_selected) {
+            s->bios_ram_selected = 1;
+            if (!s->itf_selected) {
+                register_bios_ram(s);
+            }
+        }
+    } else {
+        if (s->bios_ram_selected) {
+            s->bios_ram_selected = 0;
+            if (!s->itf_selected) {
+                register_bios_rom(s);
+            }
+        }
+    }
+}
+
+static void ioport_63c_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    MemoryState *s = opaque;
+
+    switch (data & 0x03) {
+    case 1:
+        if (s->d8000_bank != D8000_BANK_IDE) {
+            register_ide_rom(s);
+        }
+        break;
+    case 2:
+        if (s->d8000_bank != D8000_BANK_PCI) {
+            register_pci_rom(s);
+        }
+        break;
+    case 3:
+        if (s->d8000_bank != D8000_BANK_PnP) {
+            register_pnp_rom(s);
+        }
+        break;
+    }
+    s->d8000_bank = data & 0x03;
+}
+
+static uint32_t ioport_63c_read(void *opaque, uint32_t addr)
+{
+    MemoryState *s = opaque;
+
+    return s->d8000_bank;
+}
+
+static uint32_t ioport_63d_read(void *opaque, uint32_t addr)
+{
+    return 0x04;
+}
+
+static void ioport_1e8e_write(void *opaque, uint32_t addr, uint32_t data)
+{
+    MemoryState *s = opaque;
+
+    switch (data) {
+    case 0x80:
+        if (s->ide_ram_selected) {
+            s->ide_ram_selected = 0;
+            if (s->d8000_bank == D8000_BANK_IDE) {
+                register_ide_rom(s);
+            }
+        }
+        break;
+    case 0x81:
+        if (!s->ide_ram_selected) {
+            s->ide_ram_selected = 1;
+            if (s->d8000_bank == D8000_BANK_IDE) {
+                register_ide_rom(s);
+            }
+        }
+        break;
+    }
+}
+
+static uint32_t ioport_1e8e_read(void *opaque, uint32_t addr)
+{
+    MemoryState *s = opaque;
+
+    if (s->ide_ram_selected) {
+        return 0x81;
+    } else {
+        return 0x80;
+    }
+}
+
+static int pc98_memory_pre_load(void *opaque)
+{
+    MemoryState *s = opaque;
+
+    if (s->ram_window_map1 == 0x0a) {
+        qemu_unregister_coalesced_mmio(isa_mem_base + 0x80000, 0x20000);
+    } else if (s->ram_window_map1 == 0x0e) {
+        qemu_unregister_coalesced_mmio(isa_mem_base + 0x80000, 0x08000);
+    }
+    if (s->ram_window_map2 == 0x0a) {
+        qemu_unregister_coalesced_mmio(isa_mem_base + 0xa0000, 0x20000);
+    } else if (s->ram_window_map2 == 0x0e) {
+        qemu_unregister_coalesced_mmio(isa_mem_base + 0xa0000, 0x08000);
+    }
+    if (s->use_system_16mb) {
+        qemu_unregister_coalesced_mmio(isa_mem_base + 0xf00000, 0xc0000);
+        qemu_unregister_coalesced_mmio(isa_mem_base + 0xfe0000, 0x08000);
+    }
+    return 0;
+}
+
+static int pc98_memory_post_load(void *opaque, int version_id)
+{
+    MemoryState *s = opaque;
+    target_phys_addr_t a;
+
+    /* restore memory bank */
+    if (s->ram_window_map1 == 0x0a) {
+        register_io_memory(s, 0x80000, 0x08000, s->tvram_io_memory);
+        register_io_memory(s, 0x88000, 0x08000, s->vram_a8000_io_memory);
+        register_io_memory(s, 0x90000, 0x10000, s->vram_b0000_io_memory);
+        qemu_register_coalesced_mmio(isa_mem_base + 0x80000, 0x20000);
+    } else if (s->ram_window_map1 == 0x0e) {
+        register_io_memory(s, 0x80000, 0x08000, s->vram_e0000_io_memory);
+        qemu_register_coalesced_mmio(isa_mem_base + 0x80000, 0x08000);
+        register_ram(s, 0x88000, 0x18000, 0xe8000);
+    } else {
+        register_ram(s, 0x80000, 0x20000, s->ram_window_map1 * 0x10000);
+    }
+    if (s->ram_window_map2 == 0x0a) {
+        register_io_memory(s, 0xa0000, 0x08000, s->tvram_io_memory);
+        register_io_memory(s, 0xa8000, 0x08000, s->vram_a8000_io_memory);
+        register_io_memory(s, 0xb0000, 0x10000, s->vram_b0000_io_memory);
+        qemu_register_coalesced_mmio(isa_mem_base + 0xa0000, 0x20000);
+    } else if (s->ram_window_map2 == 0x0e) {
+        register_io_memory(s, 0xa0000, 0x08000, s->vram_e0000_io_memory);
+        qemu_register_coalesced_mmio(isa_mem_base + 0xa0000, 0x08000);
+        register_ram(s, 0xa8000, 0x18000, 0xe8000);
+    } else {
+        register_ram(s, 0xa0000, 0x20000, s->ram_window_map2 * 0x10000);
+    }
+    if (s->d8000_bank == D8000_BANK_IDE) {
+        register_ide_rom(s);
+    } else if (s->d8000_bank == D8000_BANK_PCI) {
+        register_pci_rom(s);
+    } else if (s->d8000_bank == D8000_BANK_PnP) {
+        register_pnp_rom(s);
+    }
+    if (s->itf_selected) {
+        register_itf_rom(s);
+    } else if (s->bios_ram_selected) {
+        register_bios_ram(s);
+    } else {
+        register_bios_rom(s);
+    }
+    if (s->use_system_16mb) {
+        cpu_register_physical_memory(isa_mem_base + 0xf00000, 0xa0000,
+                                     s->vram_f00000_io_memory);
+        cpu_register_physical_memory(isa_mem_base + 0xfa0000, 0x08000,
+                                     s->tvram_io_memory);
+        cpu_register_physical_memory(isa_mem_base + 0xfa8000, 0x08000,
+                                     s->vram_a8000_io_memory);
+        cpu_register_physical_memory(isa_mem_base + 0xfb0000, 0x10000,
+                                     s->vram_b0000_io_memory);
+        cpu_register_physical_memory(isa_mem_base + 0xfe0000, 0x08000,
+                                     s->vram_e0000_io_memory);
+        for (a = 0xfc0000; a < 0xfe0000; a += BANK_SIZE) {
+            ram_addr_t phys_offset = cpu_get_physical_page_desc(a & 0xfffff);
+            cpu_register_physical_memory(a, BANK_SIZE, phys_offset);
+        }
+        for (a = 0xfe8000; a < 0x1000000; a += BANK_SIZE) {
+            ram_addr_t phys_offset = cpu_get_physical_page_desc(a & 0xfffff);
+            cpu_register_physical_memory(a, BANK_SIZE, phys_offset);
+        }
+        qemu_register_coalesced_mmio(isa_mem_base + 0xf00000, 0xc0000);
+        qemu_register_coalesced_mmio(isa_mem_base + 0xfe0000, 0x08000);
+    } else {
+        for (a = 0xf00000; a < 0x1000000; a += BANK_SIZE) {
+            cpu_register_physical_memory(a, BANK_SIZE,
+                                             s->ram_addr[a >> BANK_BITS]);
+        }
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_memory = {
+    .name = "pc98-mem",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_load = pc98_memory_pre_load,
+    .post_load = pc98_memory_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(ram_window_map1, MemoryState),
+        VMSTATE_UINT8(ram_window_map2, MemoryState),
+        VMSTATE_UINT8(d8000_bank, MemoryState),
+        VMSTATE_UINT8(ide_bios_enabled, MemoryState),
+        VMSTATE_UINT8(ide_ram_selected, MemoryState),
+        VMSTATE_UINT8(bios_ram_selected, MemoryState),
+        VMSTATE_UINT8(itf_selected, MemoryState),
+        VMSTATE_UINT8(use_system_16mb, MemoryState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pc98_memory_reset(void *opaque)
+{
+    MemoryState *s = opaque;
+
+    if (s->ram_window_map1 != 0x08) {
+        s->ram_window_map1 = 0x08;
+        register_ram(s, 0x80000, 0x20000, 0x80000);
+    }
+    if (s->ram_window_map2 != 0x0a) {
+        s->ram_window_map2 = 0x0a;
+        register_io_memory(s, 0xa0000, 0x08000, s->tvram_io_memory);
+        register_io_memory(s, 0xa8000, 0x08000, s->vram_a8000_io_memory);
+        register_io_memory(s, 0xb0000, 0x10000, s->vram_b0000_io_memory);
+    }
+    if (!(s->d8000_bank == D8000_BANK_IDE &&
+          s->ide_bios_enabled && s->ide_ram_selected)
+       ) {
+        s->d8000_bank = D8000_BANK_IDE;
+        s->ide_bios_enabled = 1;
+        s->ide_ram_selected = 1;
+        register_ide_rom(s);
+    }
+    if (!s->itf_selected) {
+        s->itf_selected = 1;
+        register_itf_rom(s);
+    }
+    s->bios_ram_selected = 0;
+}
+
+static int patch_itf(uint8_t *buf, const char *msg)
+{
+    uint8_t op[12] = {
+        0xbe, 0x00, 0x00, //     mov si,msg
+        0xbd, 0x00, 0x00, //     mov bp,retaddr
+        0xe9, 0x00, 0x00, //     jmp disp
+                          // retaddr:
+        0xf4,             //     hlt
+        0xeb, 0xfe,       //     jmp $-2
+    };
+    int len = strlen(msg);
+    int i, a1, a2;
+    int ret = 0;
+
+    for (a1 = 1; a1 < 0x8000 - len; a1++) {
+        for (i = 0; i < len; i++) {
+            if (buf[a1 + i] != msg[i]) {
+                break;
+            }
+        }
+        if (i == len) {
+            op[1] = (a1 - 1) & 0xff;
+            op[2] = (a1 - 1) >> 8;
+            for (a2 = 0; a2 < 0x8000 - 12; a2++) {
+                op[4] = (a2 + 9) & 0xff;
+                op[5] = (a2 + 9) >> 8;
+                for (i = 0; i < 12; i++) {
+                    if (!(op[i] == buf[a2 + i] || op[i] == 0x00)) {
+                        break;
+                    }
+                }
+                if (i == 9 || i == 12) {
+                    memset(buf + a2, 0x90, i);
+                    ret = 1;
+                }
+            }
+        }
+    }
+    return ret;
+}
+
+static void update_check_sum(uint8_t *buf)
+{
+    uint8_t l = 0, h = 0;
+    int i;
+
+    for (i = 0; i < 0x8000; i += 2) {
+        l += buf[i + 0];
+        h += buf[i + 1];
+    }
+    buf[0x7ffe] -= l;
+    buf[0x7fff] -= h;
+}
+
+void pc98_memory_init(ram_addr_t ram_size, uint8_t hd_connect)
+{
+    MemoryState *s;
+    uint8_t *buf;
+    char filename[16], *filepath;
+    uint8_t loaded = 0;
+    int i;
+    ram_addr_t a;
+
+    s = qemu_mallocz(sizeof(MemoryState));
+
+    /* memory must be initialized after vga is initialized */
+    s->tvram_io_memory = cpu_get_physical_page_desc(0xa0000);
+    s->vram_a8000_io_memory = cpu_get_physical_page_desc(0xa8000);
+    s->vram_b0000_io_memory = cpu_get_physical_page_desc(0xb0000);
+    s->vram_e0000_io_memory = cpu_get_physical_page_desc(0xe0000);
+    s->vram_f00000_io_memory = cpu_get_physical_page_desc(0xf00000);
+
+    s->ram_window_map1 = 0x08;
+    s->ram_window_map2 = 0x0a;
+    s->d8000_bank = D8000_BANK_IDE;
+    s->ide_bios_enabled = 1;
+    s->ide_ram_selected = 1;
+    s->bios_ram_selected = 0;
+    s->itf_selected = 1;
+#ifdef PC98_DONT_USE_16MB_MEM
+    s->use_system_16mb = 0;
+#else
+    s->use_system_16mb = 1;
+#endif
+
+    /* allocate RAM */
+    if (ram_size < 0x1000000) {
+        ram_size = 0x1000000; /* >= 16MB */
+    }
+    ram_size &= ~0x7fffff; /* 8MB * num */
+
+    for (i = 0; i < (0x100000 >> BANK_BITS); i++) {
+        s->mem_bank[i] = -1;
+    }
+    for (a = 0; a < 0xa0000; a += BANK_SIZE) {
+        s->ram_addr[a >> BANK_BITS] = qemu_ram_alloc(BANK_SIZE);
+    }
+    qemu_ram_alloc(0x100000 - 0xa0000); /* for PAM and SMRAM */
+    for (a = 0xa0000; a < 0x1000000; a += BANK_SIZE) {
+        s->ram_addr[a >> BANK_BITS] = qemu_ram_alloc(BANK_SIZE);
+    }
+
+    register_ram(s, 0, 0xa0000, 0);
+#ifdef PC98_DONT_USE_16MB_MEM
+    for (a = 0x100000; a < 0x1000000; a += BANK_SIZE) {
+#else
+    for (a = 0x100000; a < 0x0f00000; a += BANK_SIZE) {
+#endif
+        cpu_register_physical_memory(a, BANK_SIZE, s->ram_addr[a >> BANK_BITS]);
+    }
+    if (ram_size > 0x1000000) {
+        ram_addr_t ram_addr = qemu_ram_alloc(ram_size - 0x1000000);
+        cpu_register_physical_memory(0x1000000, ram_size - 0x1000000, ram_addr);
+    }
+
+    /* BIOS load */
+    for (a = 0; a < TOTAL_ROM_SIZE; a += BANK_SIZE) {
+        s->rom_addr[a >> BANK_BITS] = qemu_ram_alloc(BANK_SIZE);
+    }
+    buf = qemu_malloc(TOTAL_ROM_SIZE);
+    memset(buf, 0xff, TOTAL_ROM_SIZE);
+
+    for (i = 0; i < ROM_BANK_NUM; i++) {
+        sprintf(filename, BANK_FILE_NAME, i);
+        filepath = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename);
+        if (filepath) {
+            if (load_image(filepath, buf + BANK_FILE_SIZE * i) == BANK_FILE_SIZE) {
+                loaded |= (1 << i);
+            }
+            qemu_free(filepath);
+        }
+    }
+    filepath = qemu_find_file(QEMU_FILE_TYPE_BIOS, PCI_FILE_NAME);
+    if (filepath) {
+        if (load_image(filepath, buf + PCI_BIOS_OFS) == PCI_FILE_SIZE) {
+            loaded |= (1 << PCI_ROM_BANK);
+        }
+        qemu_free(filepath);
+    }
+    filepath = qemu_find_file(QEMU_FILE_TYPE_BIOS, IDE_FILE_NAME);
+    if (filepath) {
+        if (load_image(filepath, buf + IDE_BIOS_OFS) == IDE_FILE_SIZE) {
+            loaded |= (1 << IDE_ROM_BANK);
+        }
+        qemu_free(filepath);
+    }
+    filepath = qemu_find_file(QEMU_FILE_TYPE_BIOS, ITF_FILE_NAME);
+    if (filepath) {
+        if (load_image(filepath, buf + ITF_OFS) == ITF_FILE_SIZE) {
+            loaded |= (1 << ITF_ROM_BANK);
+        }
+        qemu_free(filepath);
+    }
+    filepath = qemu_find_file(QEMU_FILE_TYPE_BIOS, BIOS_FILE_NAME);
+    if (filepath) {
+        if (load_image(filepath, buf + BIOS_OFS) == BIOS_FILE_SIZE) {
+            loaded |= (7 << BIOS_ROM_BANK);
+        }
+        qemu_free(filepath);
+    }
+    if ((loaded & REQUIRED_ROM_BANK) == REQUIRED_ROM_BANK) {
+        /* ITF: disable hardware check */
+        static const char msg[][32] = {
+            "TIMER INTERRUPT ERROR",
+            "EXTENDED GVRAM ERROR",
+            /* end of array */
+            "",
+        };
+        int patched = 0;
+        for (i = 0; msg[i][0] != '\0'; i++) {
+            patched |= patch_itf(buf + ITF_OFS, msg[i]);
+        }
+        if (patched) {
+            update_check_sum(buf + ITF_OFS);
+        }
+        /* BIOS: disable PnP BIOS */
+        for (a = 0x8000; a < 0x18000; a += 0x10) {
+            uint8_t *p = buf + BIOS_OFS + a;
+            if (p[0] == 0x24 && p[1] == 'P' && p[2] == 'n' && p[3] == 'P') {
+                p[0] = 'n';
+                p[2] = 0x24;
+                break;
+            }
+        }
+        /* copy to rom_addr */
+        for (a = 0; a < TOTAL_ROM_SIZE; a += BANK_SIZE) {
+            memcpy(qemu_get_ram_ptr(s->rom_addr[a >> BANK_BITS]), buf + a, BANK_SIZE);
+        }
+        s->ide_bios_loaded = ((loaded & (1 << IDE_ROM_BANK)) != 0);
+    } else {
+        fprintf(stderr, "qemu: could not load PC-9821 BIOS\n");
+        exit(1);
+    }
+    qemu_free(buf);
+
+    register_rom(s, 0xc0000, 0x18000, NONE_OFS);
+    register_ide_rom(s);
+    register_itf_rom(s);
+
+    register_ioport_write(0x43b, 1, 1, ioport_43b_write, s);
+    register_ioport_read(0x43b, 1, 1, ioport_43b_read, s);
+    register_ioport_write(0x43d, 1, 1, ioport_43d_write, s);
+    register_ioport_write(0x461, 1, 1, ioport_461_write, s);
+    register_ioport_read(0x461, 1, 1, ioport_461_read, s);
+    register_ioport_write(0x463, 1, 1, ioport_463_write, s);
+    register_ioport_read(0x463, 1, 1, ioport_463_read, s);
+    register_ioport_write(0x53d, 1, 1, ioport_53d_write, s);
+    register_ioport_write(0x63c, 1, 1, ioport_63c_write, s);
+    register_ioport_read(0x63c, 1, 1, ioport_63c_read, s);
+    register_ioport_read(0x63d, 1, 1, ioport_63d_read, s);
+    register_ioport_write(0x1e8e, 1, 1, ioport_1e8e_write, s);
+    register_ioport_read(0x1e8e, 1, 1, ioport_1e8e_read, s);
+
+    vmstate_register(-1, &vmstate_memory, s);
+    qemu_register_reset(pc98_memory_reset, s);
+
+    /* initialize done */
+    s->hd_connect = hd_connect;
+    s->init_done = 1;
+}