diff --git a/src/chipset/compaq_386.c b/src/chipset/compaq_386.c new file mode 100644 index 000000000..0f90cc6af --- /dev/null +++ b/src/chipset/compaq_386.c @@ -0,0 +1,767 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Emulation of the Compaq 386 memory controller. + * + * Authors: Miran Grca, + * + * Copyright 2023 Miran Grca. + */ +#include +#include +#include +#include +#include +#include +#include <86box/86box.h> +#include "cpu.h" +#include <86box/io.h> +#include <86box/timer.h> +#include <86box/pit.h> +#include <86box/mem.h> +#include <86box/rom.h> +#include <86box/device.h> +#include <86box/keyboard.h> +#include <86box/fdd.h> +#include <86box/fdc.h> +#include <86box/fdc_ext.h> +#include <86box/hdc.h> +#include <86box/hdc_ide.h> +#include <86box/machine.h> +#include <86box/video.h> +#include <86box/vid_cga.h> +#include <86box/vid_cga_comp.h> +#include <86box/plat_unused.h> + +#define RAM_DIAG_L_BASE_MEM_640KB 0x00 +#define RAM_DIAG_L_BASE_MEM_INV 0x10 +#define RAM_DIAG_L_BASE_MEM_512KB 0x20 +#define RAM_DIAG_L_BASE_MEM_256KB 0x30 +#define RAM_DIAG_L_BASE_MEM_MASK 0x30 +#define RAM_DIAG_L_PERMA_BITS 0x80 + +#define RAM_DIAG_H_SYS_RAM_4MB 0x01 +#define RAM_DIAG_H_SYS_RAM_1MB 0x02 +#define RAM_DIAG_H_SYS_RAM_NONE 0x03 +#define RAM_DIAG_H_SYS_RAM_MASK 0x03 +#define RAM_DIAG_H_MOD_A_RAM_4MB 0x04 +#define RAM_DIAG_H_MOD_A_RAM_1MB 0x08 +#define RAM_DIAG_H_MOD_A_RAM_NONE 0x0c +#define RAM_DIAG_H_MOD_A_RAM_MASK 0x0c +#define RAM_DIAG_H_MOD_B_RAM_4MB 0x10 +#define RAM_DIAG_H_MOD_B_RAM_1MB 0x20 +#define RAM_DIAG_H_MOD_B_RAM_NONE 0x30 +#define RAM_DIAG_H_MOD_B_RAM_MASK 0x30 +#define RAM_DIAG_H_MOD_C_RAM_4MB 0x40 +#define RAM_DIAG_H_MOD_C_RAM_1MB 0x80 +#define RAM_DIAG_H_MOD_C_RAM_NONE 0xc0 +#define RAM_DIAG_H_MOD_C_RAM_MASK 0xc0 + +#define MEM_STATE_BUS 0x00 +#define MEM_STATE_SYS 0x01 +#define MEM_STATE_SYS_RELOC 0x02 +#define MEM_STATE_MOD_A 0x04 +#define MEM_STATE_MOD_B 0x08 +#define MEM_STATE_MOD_C 0x10 +#define MEM_STATE_MASK (MEM_STATE_SYS | MEM_STATE_MOD_A | MEM_STATE_MOD_B | MEM_STATE_MOD_C) +#define MEM_STATE_WP 0x20 + +typedef struct cpq_ram_t { + uint8_t wp; + + uint32_t phys_base; + uint32_t virt_base; + + mem_mapping_t mapping; +} cpq_ram_t; + +typedef struct cpq_386_t { + uint8_t regs[8]; + + uint8_t old_state[256]; + uint8_t mem_state[256]; + + uint32_t ram_bases[4]; + + uint32_t ram_sizes[4]; + uint32_t ram_map_sizes[4]; + + cpq_ram_t ram[4][64]; + cpq_ram_t high_ram[16]; + + mem_mapping_t regs_mapping; +} cpq_386_t; + +static uint8_t +cpq_read_ram(uint32_t addr, void *priv) +{ + cpq_ram_t *dev = (cpq_ram_t *) priv; + uint8_t ret = 0xff; + uint32_t old = addr; + + addr = (addr - dev->virt_base) + dev->phys_base; + + if (addr < (mem_size << 10)) + ret = mem_read_ram(addr, priv); + + return ret; +} + +static uint16_t +cpq_read_ramw(uint32_t addr, void *priv) +{ + cpq_ram_t *dev = (cpq_ram_t *) priv; + uint16_t ret = 0xffff; + uint32_t old = addr; + + addr = (addr - dev->virt_base) + dev->phys_base; + + if (addr < (mem_size << 10)) + ret = mem_read_ramw(addr, priv); + + return ret; +} + +static uint32_t +cpq_read_raml(uint32_t addr, void *priv) +{ + cpq_ram_t *dev = (cpq_ram_t *) priv; + uint32_t ret = 0xffffffff; + uint32_t old = addr; + + addr = (addr - dev->virt_base) + dev->phys_base; + + if (addr < (mem_size << 10)) + ret = mem_read_raml(addr, priv); + + return ret; +} + +static void +cpq_write_ram(uint32_t addr, uint8_t val, void *priv) +{ + cpq_ram_t *dev = (cpq_ram_t *) priv; + uint32_t old = addr; + + addr = (addr - dev->virt_base) + dev->phys_base; + + if (!dev->wp && (addr < (mem_size << 10))) + mem_write_ram(addr, val, priv); +} + +static void +cpq_write_ramw(uint32_t addr, uint16_t val, void *priv) +{ + cpq_ram_t *dev = (cpq_ram_t *) priv; + uint32_t old = addr; + + addr = (addr - dev->virt_base) + dev->phys_base; + + if (!dev->wp && (addr < (mem_size << 10))) + mem_write_ramw(addr, val, priv); +} + +static void +cpq_write_raml(uint32_t addr, uint32_t val, void *priv) +{ + cpq_ram_t *dev = (cpq_ram_t *) priv; + uint32_t old = addr; + + addr = (addr - dev->virt_base) + dev->phys_base; + + if (!dev->wp && (addr < (mem_size << 10))) + mem_write_raml(addr, val, priv); +} + +static uint8_t +cpq_read_regs(uint32_t addr, void *priv) +{ + cpq_386_t *dev = (cpq_386_t *) priv; + uint8_t ret = 0xff; + + addr &= 0x00000fff; + + switch (addr) { + case 0x00000000: + case 0x00000001: + /* RAM Diagnostics (Read Only) */ + case 0x00000002: + case 0x00000003: + /* RAM Setup Port (Read/Write) */ + ret = dev->regs[addr]; + break; + } + + return ret; +} + +static uint16_t +cpq_read_regsw(uint32_t addr, void *priv) +{ + cpq_386_t *dev = (cpq_386_t *) priv; + uint16_t ret = 0xffff; + + ret = cpq_read_regs(addr, priv); + ret |= (((uint16_t) cpq_read_regs(addr + 1, priv)) << 8); + + return ret; +} + +static uint32_t +cpq_read_regsl(uint32_t addr, void *priv) +{ + cpq_386_t *dev = (cpq_386_t *) priv; + uint32_t ret = 0xffffffff; + + ret = cpq_read_regsw(addr, priv); + ret |= (((uint32_t) cpq_read_regsw(addr + 2, priv)) << 16); + + return ret; +} + +static void +cpq_recalc_state(cpq_386_t *dev, uint8_t i) +{ + uint32_t addr; + + addr = ((uint32_t) i) << 16; + if (dev->mem_state[i] == 0x00) + mem_set_mem_state(addr, 0x00010000, MEM_READ_EXTANY | MEM_WRITE_EXTANY); + else if (dev->mem_state[i] == MEM_STATE_WP) + mem_set_mem_state(addr, 0x00010000, MEM_READ_EXTANY | MEM_WRITE_DISABLED); + else if (dev->mem_state[i] & MEM_STATE_WP) + mem_set_mem_state(addr, 0x00010000, MEM_READ_INTERNAL | MEM_WRITE_DISABLED); + else + mem_set_mem_state(addr, 0x00010000, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL); + + dev->old_state[i] = dev->mem_state[i]; +} + +static void +cpq_recalc_states(cpq_386_t *dev) +{ + /* Recalculate the entire 16 MB space. */ + for (uint16_t i = 0; i < 256; i++) { + if (dev->mem_state[i] != dev->old_state[i]) + cpq_recalc_state(dev, i); + } + + flushmmucache_nopc(); +} + +static void +cpq_recalc_cache(cpq_386_t *dev) +{ + cpu_cache_ext_enabled = (dev->regs[0x00000002] & 0x40); + cpu_update_waitstates(); +} + +static void +cpq_recalc_ram(cpq_386_t *dev) +{ + uint8_t sys_ram = (dev->regs[0x00000001] & RAM_DIAG_H_SYS_RAM_MASK) & 0x01; + uint8_t setup_port = dev->regs[0x00000002] & 0x0f; + uint8_t sys_min_high = sys_ram ? 0xfa : 0xf4; + uint8_t ram_states[4] = { MEM_STATE_SYS, MEM_STATE_MOD_A, + MEM_STATE_MOD_B, MEM_STATE_MOD_C }; + uint8_t ram_bases[4][2][16] = { { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x10, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 } }, + { { 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00 }, + { 0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 } }, + { { 0x00, 0x00, 0x00, 0x20, 0x20, 0x00, 0x20, 0x20, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 }, + { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 } }, + { { 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x00, 0x00 }, + { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x90, 0x00, 0x00, 0xc0, 0xc0, 0xc0 } } }; + uint8_t ram_sizes[4][2][16] = { { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x30, 0x00, 0x10, 0x20, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 } }, + { { 0x00, 0x00, 0x10, 0x10, 0x10, 0x40, 0x10, 0x10, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00 }, + { 0x40, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 } }, + { { 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x40, 0x40, + 0x30, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00 }, + { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, + 0x00, 0x10, 0x10, 0x30, 0x40, 0x40, 0x40, 0x40 } }, + { { 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x10, 0x20, 0x30, 0x40, 0x00, 0x00 }, + { 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x20, 0x30 } } }; + uint8_t size; + uint8_t start; + uint8_t end; + uint8_t k; + uint32_t virt_base; + uint32_t virt_addr; + uint32_t phys_addr; + cpq_ram_t *cram; + + for (uint16_t i = 0x10; i < sys_min_high; i++) + dev->mem_state[i] &= ~MEM_STATE_MASK; + + for (uint8_t i = 0; i < 4; i++) { + for (uint8_t j = 0; j <= 64; j++) { + if ((i >= 1) || (j >= 0x10)) + mem_mapping_disable(&dev->ram[i][j].mapping); + } + } + + for (uint8_t i = 0; i < 4; i++) { + size = ram_sizes[i][sys_ram][setup_port]; + if (size > 0x00) { + start = ram_bases[i][sys_ram][setup_port]; + end = start + (size - 1); + + virt_base = ((uint32_t) start) << 16; + + for (uint16_t j = start; j <= end; j++) { + k = j - start; + if (i == 0) + k += 0x10; + + cram = &(dev->ram[i][k]); + + dev->mem_state[j] |= ram_states[i]; + + cram->virt_base = ((uint32_t) j) << 16; + cram->phys_base = cram->virt_base - virt_base + dev->ram_bases[i]; + + mem_mapping_set_addr(&cram->mapping, cram->virt_base, 0x00010000); + mem_mapping_set_exec(&cram->mapping, &(ram[cram->phys_base])); + } + } + } + + /* Recalculate the entire 16 MB space. */ + cpq_recalc_states(dev); +} + +static void +cpq_write_regs(uint32_t addr, uint8_t val, void *priv) +{ + cpq_386_t *dev = (cpq_386_t *) priv; + + addr &= 0x00000fff; + + switch (addr) { + case 0x00000000: + case 0x00000001: + /* RAM Relocation (Write Only) */ + dev->regs[addr + 4] = val; + if (addr == 0x00000000) { + dev->mem_state[0x0e] &= ~(MEM_STATE_SYS | MEM_STATE_WP); + dev->mem_state[0x0f] &= ~(MEM_STATE_SYS | MEM_STATE_WP); + dev->mem_state[0xfe] &= ~MEM_STATE_WP; + dev->mem_state[0xff] &= ~MEM_STATE_WP; + if (!(val & 0x01)) { + dev->mem_state[0x0e] |= MEM_STATE_SYS; + dev->mem_state[0x0f] |= MEM_STATE_SYS; + } + if (!(val & 0x02)) { + dev->mem_state[0x0e] |= MEM_STATE_WP; + dev->mem_state[0x0f] |= MEM_STATE_WP; + dev->mem_state[0xfe] |= MEM_STATE_WP; + dev->mem_state[0xff] |= MEM_STATE_WP; + } + cpq_recalc_state(dev, 0x0e); + cpq_recalc_state(dev, 0x0f); + cpq_recalc_state(dev, 0xfe); + cpq_recalc_state(dev, 0xff); + flushmmucache_nopc(); + } + break; + case 0x00000002: + case 0x00000003: + /* RAM Setup Port (Read/Write) */ + dev->regs[addr] = val; + if (addr == 0x00000002) { + cpq_recalc_ram(dev); + cpq_recalc_cache(dev); + } + break; + } +} + +static void +cpq_write_regsw(uint32_t addr, uint16_t val, void *priv) +{ + cpq_386_t *dev = (cpq_386_t *) priv; + + cpq_write_regs(addr, val & 0xff, priv); + cpq_write_regs(addr + 1, (val >> 8) & 0xff, priv); +} + +static void +cpq_write_regsl(uint32_t addr, uint32_t val, void *priv) +{ + cpq_386_t *dev = (cpq_386_t *) priv; + + cpq_write_regsw(addr, val & 0xff, priv); + cpq_write_regsw(addr + 2, (val >> 16) & 0xff, priv); +} + +static void +compaq_ram_init(cpq_ram_t *dev) +{ + mem_mapping_add(&dev->mapping, + 0x00000000, + 0x00010000, + cpq_read_ram, + cpq_read_ramw, + cpq_read_raml, + cpq_write_ram, + cpq_write_ramw, + cpq_write_raml, + NULL, + MEM_MAPPING_INTERNAL, + dev); + + mem_mapping_disable(&dev->mapping); +} + +static void +compaq_ram_diags_parse(cpq_386_t *dev) +{ + uint8_t val = dev->regs[0x00000001]; + uint32_t accum = 0x00100000; + + val; + + for (uint8_t i = 0; i < 4; i++) { + dev->ram_bases[i] = accum; + + switch (val & 0x03) { + case RAM_DIAG_H_SYS_RAM_1MB: + dev->ram_sizes[i] = 0x00100000; + break; + case RAM_DIAG_H_SYS_RAM_4MB: + dev->ram_sizes[i] = 0x00400000; + break; + } + if (i == 0) + dev->ram_sizes[i] -= 0x00100000; + + dev->ram_map_sizes[i] = dev->ram_sizes[i]; + accum += dev->ram_sizes[i]; + + if (accum >= (mem_size << 10)) { + dev->ram_sizes[i] = (mem_size << 10) - dev->ram_bases[i]; + break; + } + + val >>= 2; + } +} + +static void +compaq_recalc_base_ram(cpq_386_t *dev) +{ + uint8_t base_mem = dev->regs[0x00000000] & RAM_DIAG_L_BASE_MEM_MASK; + uint8_t sys_ram = dev->regs[0x00000001] & RAM_DIAG_H_SYS_RAM_MASK; + uint8_t low_start = 0x00; + uint8_t low_end = 0x00; + uint8_t high_start = 0x00; + uint8_t high_end = 0x00; + uint32_t phys_addr = 0x00000000; + uint32_t virt_addr = 0x00000000; + cpq_ram_t *cram; + + switch (base_mem) { + case RAM_DIAG_L_BASE_MEM_256KB: + switch (sys_ram) { + case RAM_DIAG_H_SYS_RAM_1MB: + low_start = 0x00; + low_end = 0x03; + high_start = 0xf4; + high_end = 0xff; + break; + case RAM_DIAG_H_SYS_RAM_4MB: + low_start = 0x00; + low_end = 0x03; + high_start = 0xfa; + high_end = 0xff; + break; + default: + fatal("Compaq 386 - Invalid configuation: %02X %02X\n", base_mem, sys_ram); + return; + } + break; + case RAM_DIAG_L_BASE_MEM_512KB: + switch (sys_ram) { + case RAM_DIAG_H_SYS_RAM_1MB: + low_start = 0x00; + low_end = 0x07; + high_start = 0xf8; + high_end = 0xff; + break; + case RAM_DIAG_H_SYS_RAM_4MB: + low_start = 0x00; + low_end = 0x07; + high_start = 0xfa; + high_end = 0xff; + break; + default: + fatal("Compaq 386 - Invalid configuation: %02X %02X\n", base_mem, sys_ram); + return; + } + break; + case RAM_DIAG_L_BASE_MEM_640KB: + switch (sys_ram) { + case RAM_DIAG_H_SYS_RAM_1MB: + low_start = 0x00; + low_end = 0x09; + high_start = 0xfa; + high_end = 0xff; + break; + case RAM_DIAG_H_SYS_RAM_4MB: + low_start = 0x00; + low_end = 0x09; + high_start = 0xfa; + high_end = 0xff; + break; + default: + fatal("Compaq 386 - Invalid configuation: %02X %02X\n", base_mem, sys_ram); + return; + } + break; + default: + fatal("Compaq 386 - Invalid configuation: %02X %02X\n", base_mem, sys_ram); + return; + } + + switch (sys_ram) { + case RAM_DIAG_H_SYS_RAM_1MB: + if (mem_size < 1024) + dev->regs[0x00000002] = 0x01; + else if (mem_size == 8192) + dev->regs[0x00000002] = 0x09; + else if (mem_size >= 11264) + dev->regs[0x00000002] = 0x0d; + else + dev->regs[0x00000002] = (mem_size >> 10); + break; + case RAM_DIAG_H_SYS_RAM_4MB: + if (mem_size < 4096) + dev->regs[0x00000002] = 0x04; + else if (mem_size == 11264) + dev->regs[0x00000002] = 0x0c; + else if (mem_size >= 16384) + dev->regs[0x00000002] = 0x00; + else if (mem_size > 13312) + dev->regs[0x00000002] = 0x0d; + else + dev->regs[0x00000002] = (mem_size >> 10); + break; + default: + fatal("Compaq 386 - Invalid configuation: %02X\n", sys_ram); + return; + } + + /* The base 640 kB. */ + for (uint8_t i = low_start; i <= low_end; i++) { + cram = &(dev->ram[0][i]); + + cram->phys_base = cram->virt_base = ((uint32_t) i) << 16; + dev->mem_state[i] |= MEM_STATE_SYS; + + mem_mapping_set_addr(&cram->mapping, cram->virt_base, 0x00010000); + mem_mapping_set_exec(&cram->mapping, &(ram[cram->phys_base])); + + cpq_recalc_state(dev, i); + } + + /* The relocated 128 kB. */ + for (uint8_t i = 0x0e; i <= 0x0f; i++) { + cram = &(dev->ram[0][i]); + + cram->phys_base = cram->virt_base = ((uint32_t) i) << 16; + + mem_mapping_set_addr(&cram->mapping, cram->virt_base, 0x00010000); + mem_mapping_set_exec(&cram->mapping, &(ram[cram->phys_base])); + } + + /* Blocks FA-FF. */ + for (uint16_t i = high_start; i <= high_end; i++) { + cram = &(dev->high_ram[i & 0x0f]); + + cram->phys_base = ((uint32_t) (i & 0x0f)) << 16; + cram->virt_base = ((uint32_t) i) << 16; + dev->mem_state[i] |= MEM_STATE_SYS; + + mem_mapping_set_addr(&cram->mapping, cram->virt_base, 0x00010000); + mem_mapping_set_exec(&cram->mapping, &(ram[cram->phys_base])); + + cpq_recalc_state(dev, i); + } +} + +static void +compaq_386_close(void *priv) +{ + cpq_386_t *dev = (cpq_386_t *) priv; + + free(dev); +} + +static void * +compaq_386_init(const device_t *info) +{ + cpq_386_t *dev = (cpq_386_t *) calloc(1, sizeof(cpq_386_t)); + + mem_mapping_add(&dev->regs_mapping, + 0x80c00000, + 0x00001000, + cpq_read_regs, + cpq_read_regsw, + cpq_read_regsl, + cpq_write_regs, + cpq_write_regsw, + cpq_write_regsl, + NULL, + MEM_MAPPING_INTERNAL, + dev); + + mem_set_mem_state(0x80c00000, 0x00001000, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL); + + dev->regs[0x00000000] = RAM_DIAG_L_PERMA_BITS; + if (mem_size >= 640) + dev->regs[0x00000000] |= RAM_DIAG_L_BASE_MEM_640KB; + else if (mem_size >= 512) + dev->regs[0x00000000] |= RAM_DIAG_L_BASE_MEM_512KB; + else if (mem_size >= 256) + dev->regs[0x00000000] |= RAM_DIAG_L_BASE_MEM_256KB; + else + dev->regs[0x00000000] |= RAM_DIAG_L_BASE_MEM_INV; + /* Indicate no parity error. */ + dev->regs[0x00000000] |= 0x0f; + + if (mem_size >= 1024) { + switch (mem_size) { + case 1024: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_NONE | + RAM_DIAG_H_MOD_B_RAM_NONE | RAM_DIAG_H_MOD_C_RAM_NONE; + break; + case 2048: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_NONE | + RAM_DIAG_H_MOD_B_RAM_NONE | RAM_DIAG_H_MOD_C_RAM_NONE; + break; + case 3072: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_NONE | + RAM_DIAG_H_MOD_B_RAM_NONE | RAM_DIAG_H_MOD_C_RAM_NONE; + break; + case 4096: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_NONE | + RAM_DIAG_H_MOD_B_RAM_NONE | RAM_DIAG_H_MOD_C_RAM_NONE; + break; + case 5120: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_1MB | + RAM_DIAG_H_MOD_B_RAM_NONE | RAM_DIAG_H_MOD_C_RAM_NONE; + break; + case 6144: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_1MB | + RAM_DIAG_H_MOD_B_RAM_1MB | RAM_DIAG_H_MOD_C_RAM_NONE; + break; + case 7168: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_1MB | + RAM_DIAG_H_MOD_B_RAM_1MB | RAM_DIAG_H_MOD_C_RAM_1MB; + break; + case 8192: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_4MB | + RAM_DIAG_H_MOD_B_RAM_NONE | RAM_DIAG_H_MOD_C_RAM_NONE; + break; + case 9216: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_4MB | + RAM_DIAG_H_MOD_B_RAM_1MB | RAM_DIAG_H_MOD_C_RAM_NONE; + break; + case 10240: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_4MB | + RAM_DIAG_H_MOD_B_RAM_1MB | RAM_DIAG_H_MOD_C_RAM_1MB; + break; + case 11264: + case 12288: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_4MB | + RAM_DIAG_H_MOD_B_RAM_4MB | RAM_DIAG_H_MOD_C_RAM_NONE; + break; + case 13312: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_4MB | + RAM_DIAG_H_MOD_B_RAM_4MB | RAM_DIAG_H_MOD_C_RAM_1MB; + break; + case 14336: + case 15360: + case 16384: + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_4MB | RAM_DIAG_H_MOD_A_RAM_4MB | + RAM_DIAG_H_MOD_B_RAM_4MB | RAM_DIAG_H_MOD_C_RAM_4MB; + break; + } + } else + dev->regs[0x00000001] = RAM_DIAG_H_SYS_RAM_1MB | RAM_DIAG_H_MOD_A_RAM_NONE | + RAM_DIAG_H_MOD_B_RAM_NONE | RAM_DIAG_H_MOD_C_RAM_NONE; + + dev->regs[0x00000003] = 0xfc; + dev->regs[0x00000004] = dev->regs[0x00000005] = 0xff; + + compaq_ram_diags_parse(dev); + + mem_mapping_disable(&ram_low_mapping); + mem_mapping_disable(&ram_mid_mapping); + mem_mapping_disable(&ram_high_mapping); +#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64)) + /* Should never be the case, but you never know what a user may set. */ + if (mem_size > 1048576) + mem_mapping_disable(&ram_2gb_mapping); +#endif + + /* Initialize in reverse order for memory mapping precedence + reasons. */ + for (int8_t i = 3; i >= 0; i--) { + for (uint8_t j = 0; j < 64; j++) + compaq_ram_init(&(dev->ram[i][j])); + } + + for (uint8_t i = 0; i < 16; i++) + compaq_ram_init(&(dev->high_ram[i])); + + /* First, set the entire 256 MB of space to invalid states. */ + for (uint16_t i = 0; i < 256; i++) + dev->old_state[i] = 0xff; + + /* Then, recalculate the base RAM mappings. */ + compaq_recalc_base_ram(dev); + + /* Enable the external cache. */ + dev->regs[0x00000002] |= 0x40; + cpq_recalc_cache(dev); + + /* Recalculate the rest of the RAM mapping. */ + cpq_recalc_ram(dev); + + return dev; +} + +const device_t compaq_386_device = { + .name = "Compaq 386 Memory Control", + .internal_name = "opti391", + .flags = 0, + .local = 0, + .init = compaq_386_init, + .close = compaq_386_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +};