/* * 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. * * Implementation of the Western Digital WD76C10 chipset. * * Authors: Miran Grca, * * Copyright 2024 Miran Grca. * */ #include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include "cpu.h" #include <86box/timer.h> #include <86box/io.h> #include <86box/device.h> #include <86box/dma.h> #include <86box/fdd.h> #include <86box/fdc.h> #include <86box/hdd.h> #include <86box/hdc.h> #include <86box/hdc_ide.h> #include <86box/lpt.h> #include <86box/mem.h> #include <86box/nvr.h> #include <86box/port_92.h> #include <86box/serial.h> #include <86box/plat_fallthrough.h> #include <86box/chipset.h> /* Lock/Unlock Procedures */ #define LOCK dev->locked #define UNLOCKED !dev->locked #define WD76C10_ADDR_INVALID 0x80000000 #ifdef ENABLE_WD76C10_LOG int wd76c10_do_log = ENABLE_WD76C10_LOG; static void wd76c10_log(const char *fmt, ...) { va_list ap; if (wd76c10_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else #define wd76c10_log(fmt, ...) #endif typedef struct { uint32_t enable; uint32_t virt_addr, phys_addr; uint32_t virt_size, phys_size; } ram_bank_t; typedef struct { uint8_t enabled; uint32_t virt, phys; uint32_t size; } ems_page_t; typedef struct { uint8_t ep, p92; uint8_t vbios_states[4]; uint8_t bios_states[8]; uint8_t high_bios_states[8]; uint8_t mem_pages[1024]; uint8_t ram_state[4192]; uint16_t toggle, cpuclk, fpu_ctl, mem_ctl, split_sa, sh_wp, hmwpb, npmdmt, ems_ctl, ems_pp, ser_par_cs, rtc_disk_cs, prog_cs, pmc_in; union { uint16_t bank_base_regs[2]; uint8_t bank_bases[4]; }; uint16_t ems_page_regs[40]; int locked; uint32_t mem_top, hmwp_base; ram_bank_t ram_banks[5]; ems_page_t ems_pages[40]; mem_mapping_t ram_mapping; nvr_t *nvr; fdc_t *fdc; serial_t *uart[2]; } wd76c10_t; static uint32_t bank_sizes[4] = { 0x00020000, /* 64 Kbit X 16 = 1024 Kbit = 128 kB, 8x 8 */ 0x00080000, /* 256 Kbit X 16 = 4096 Kbit = 512 kB, 9x 9 */ 0x00200000, /* 1 Mbit X 16 = 16 Mbit = 2 MB, 10x10 */ 0x00800000 }; /* 4 Mbit X 16 = 64 Mbit = 8 MB, 11x11 */ static uint32_t wd76c10_calc_addr(wd76c10_t *dev, uint32_t addr) { uint32_t ret; uint8_t ems_page; uint8_t ems_en = (uint8_t) ((dev->ems_ctl >> 10) & 0x03); ems_page_t *ep; uint8_t en_res = (uint8_t) ((dev->ems_ctl >> 7) & 0x01); uint32_t low_boundary = (((uint32_t) (dev->ems_ctl & 0x007f)) << 17) + 131072; ram_bank_t *rb = &(dev->ram_banks[4]); addr &= 0x00ffffff; ems_page = dev->mem_pages[addr >> 14]; ep = &dev->ems_pages[ems_page]; ret = addr; /* First, do any address translation (EMS, low boundary filtering). */ if ((ems_page < 0x20) && (ems_en == 0x03)) /* Low EMS pages. */ ret = addr - ep->virt + ep->phys; else if ((ems_page >= 0x20) && (ems_page < 0x2a) && (ems_en >= 0x02) && ep->enabled) /* High EMS pages. */ ret = addr - ep->virt + ep->phys; else if (en_res && (addr >= low_boundary)) /* EMS low boundary. */ ret = WD76C10_ADDR_INVALID; /* Then, do the split. */ if (rb->enable && (ret >= rb->virt_addr) && (ret < (rb->virt_addr + rb->virt_size))) ret = ret - rb->virt_addr + rb->phys_addr; /* Then, disable the required amount of on-board memory between 128k and 640k if so requested. */ if ((ret >= dev->mem_top) && (ret < 0x000a0000)) ret = WD76C10_ADDR_INVALID; /* Then, handle the physical memory banks. */ if (ret >= (mem_size << 10)) /* The physical memory address is too high or disabled, which is invalid. */ ret = WD76C10_ADDR_INVALID; /* Otherwise, map it to the correct bank so the BIOS can auto-size it correctly. */ else for (uint8_t i = 0; i < 4; i++) { rb = &(dev->ram_banks[i]); if (rb->enable && (ret >= rb->virt_addr) && (ret < (rb->virt_addr + rb->virt_size))) { if (rb->phys_size == 0x00000000) ret = WD76C10_ADDR_INVALID; else ret = ((ret - rb->virt_addr) % rb->phys_size) + rb->phys_addr; break; } } return ret; } static uint8_t wd76c10_read_ram(uint32_t addr, void *priv) { wd76c10_t *dev = (wd76c10_t *) priv; uint8_t ret = 0xff; addr = wd76c10_calc_addr(dev, addr); if (addr != WD76C10_ADDR_INVALID) ret = mem_read_ram(addr, priv); return ret; } static uint16_t wd76c10_read_ramw(uint32_t addr, void *priv) { wd76c10_t *dev = (wd76c10_t *) priv; uint16_t ret = 0xffff; addr = wd76c10_calc_addr(dev, addr); if (addr != WD76C10_ADDR_INVALID) ret = mem_read_ramw(addr, priv); return ret; } static void wd76c10_write_ram(uint32_t addr, uint8_t val, void *priv) { wd76c10_t *dev = (wd76c10_t *) priv; addr = wd76c10_calc_addr(dev, addr); if (addr != WD76C10_ADDR_INVALID) mem_write_ram(addr, val, priv); } static void wd76c10_write_ramw(uint32_t addr, uint16_t val, void *priv) { wd76c10_t *dev = (wd76c10_t *) priv; addr = wd76c10_calc_addr(dev, addr); if (addr != WD76C10_ADDR_INVALID) mem_write_ramw(addr, val, priv); } static void wd76c10_set_mem_state(wd76c10_t *dev, uint32_t base, uint32_t size, uint32_t access, uint8_t present) { mem_set_mem_state_both(base, size, access); for (uint32_t i = base; i < (base + size); i += 4096) dev->ram_state[i >> 12] = present; } static void wd76c10_recalc_exec(wd76c10_t *dev, uint32_t base, uint32_t size) { uint32_t logical_addr = wd76c10_calc_addr(dev, base); void *exec; if (logical_addr != WD76C10_ADDR_INVALID) exec = &(ram[logical_addr]); else exec = NULL; for (uint32_t i = base; i < (base + size); i += 4096) if (dev->ram_state[i >> 12]) _mem_exec[i >> 12] = exec; if (cpu_use_exec) flushmmucache_nopc(); } static void wd76c10_banks_recalc(wd76c10_t *dev) { for (uint8_t i = 0; i < 4; i++) { ram_bank_t *rb = &(dev->ram_banks[i]); uint8_t bit = i << 1; rb->virt_size = bank_sizes[(dev->mem_ctl >> bit) & 0x03]; bit = i + 12; rb->enable = (dev->split_sa >> bit) & 0x01; rb->virt_addr = ((uint32_t) dev->bank_bases[i]) << 17; if (cpu_use_exec) wd76c10_recalc_exec(dev, rb->virt_addr, rb->virt_size); } } static void wd76c10_split_recalc(wd76c10_t *dev) { uint32_t sp_size = (dev->split_sa >> 8) & 0x03; uint32_t split_size = ((sp_size - 1) * 65536); ram_bank_t *rb = &(dev->ram_banks[4]); if (rb->enable && (rb->virt_size != 0x00000000)) { wd76c10_set_mem_state(dev, rb->virt_addr, rb->virt_size, MEM_READ_EXTANY | MEM_WRITE_EXTANY, 0); if (cpu_use_exec) wd76c10_recalc_exec(dev, rb->virt_addr, rb->virt_size); } rb->virt_addr = ((uint32_t) ((dev->split_sa >> 2) & 0x3f)) << 19; switch (sp_size) { case 0x00: rb->virt_size = 0x00000000; break; default: rb->virt_size = 256 * 1024 + split_size; break; } rb->enable = !!sp_size; if (rb->enable && (rb->virt_size != 0x00000000)) { wd76c10_set_mem_state(dev, rb->virt_addr, rb->virt_size, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL, 1); if (cpu_use_exec) wd76c10_recalc_exec(dev, rb->virt_addr, rb->virt_size); } } static void wd76c10_dis_mem_recalc(wd76c10_t *dev) { uint8_t dis_mem = (uint8_t) ((dev->sh_wp >> 14) & 0x03); uint32_t mem_top; switch (dis_mem) { case 0x00: default: mem_top = 640 * 1024; break; case 0x01: mem_top = 512 * 1024; break; case 0x02: mem_top = 256 * 1024; break; case 0x03: mem_top = 128 * 1024; break; } dev->mem_top = mem_top; if (cpu_use_exec) wd76c10_recalc_exec(dev, 128 * 1024, (640 - 128) * 1024); } static void wd76c10_shadow_ram_do_recalc(wd76c10_t *dev, uint8_t *new_st, uint8_t *old_st, uint8_t min, uint8_t max, uint32_t addr) { uint32_t base = 0x00000000; int flags = 0; for (uint8_t i = min; i < max; i++) { if (new_st[i] != old_st[i]) { old_st[i] = new_st[i]; base = addr + ((uint32_t) i) * 0x00004000; flags = (new_st[i] & 0x01) ? MEM_READ_INTERNAL : ((new_st[i] & 0x04) ? MEM_READ_ROMCS : MEM_READ_EXTERNAL); flags |= (new_st[i] & 0x02) ? MEM_WRITE_INTERNAL : ((new_st[i] & 0x04) ? MEM_WRITE_ROMCS : MEM_WRITE_EXTERNAL); wd76c10_set_mem_state(dev, base, 0x00004000, flags, new_st[i] & 0x01); if (cpu_use_exec) wd76c10_recalc_exec(dev, base, 0x000040000); } } } static void wd76c10_shadow_ram_recalc(wd76c10_t *dev) { uint8_t vbios_states[4] = { 0 }; uint8_t bios_states[8] = { 0 }; uint8_t high_bios_states[8] = { 0 }; uint8_t wp = (uint8_t) ((dev->sh_wp >> 12) & 0x01); uint8_t shd = (uint8_t) ((dev->sh_wp >> 8) & 0x03); uint8_t x_mem = (uint8_t) ((dev->sh_wp >> 7) & 0x01); uint8_t vb_siz = (uint8_t) ((dev->sh_wp >> 4) & 0x03); uint8_t vb_top = vb_siz + 1; uint8_t rom_typ = (uint8_t) ((dev->sh_wp >> 2) & 0x03); switch (shd) { case 0x03: for (uint8_t i = 0; i < vb_top; i++) { vbios_states[i] |= 0x01; /* Read. */ if (!wp) vbios_states[i] |= 0x02; /* Write. */ } if (x_mem) { for (uint8_t i = 2; i < 4; i++) bios_states[i] |= 0x03; /* Read/write. */ } fallthrough; case 0x01: for (uint8_t i = 4; i < 8; i++) { bios_states[i] |= 0x01; /* Read. */ if (!wp) bios_states[i] |= 0x02; /* Write. */ } break; case 0x02: for (uint8_t i = 0; i < 8; i++) { bios_states[i] |= 0x01; /* Read. */ if (!wp) bios_states[i] |= 0x02; /* Write. */ } break; } switch (rom_typ) { case 0x00: for (uint8_t i = 0; i < 8; i++) { bios_states[i] |= 0x04; /* CSPROM#. */ high_bios_states[i] |= 0x04; /* CSPROM#. */ } break; case 0x02: for (uint8_t i = 0; i < vb_top; i++) vbios_states[i] |= 0x04; /* CSPROM#. */ fallthrough; case 0x01: for (uint8_t i = 4; i < 8; i++) { bios_states[i] |= 0x04; /* CSPROM#. */ high_bios_states[i] |= 0x04; /* CSPROM#. */ } break; } wd76c10_shadow_ram_do_recalc(dev, vbios_states, dev->vbios_states, 0, 4, 0x000c0000); wd76c10_shadow_ram_do_recalc(dev, bios_states, dev->bios_states, 0, 8, 0x000e0000); /* This is not shadowed, but there is a CSPROM# (= ROMCS#) toggle. */ wd76c10_shadow_ram_do_recalc(dev, high_bios_states, dev->high_bios_states, 0, 8, 0x00fe0000); flushmmucache_nopc(); } static void wd76c10_high_mem_wp_recalc(wd76c10_t *dev) { uint8_t hm_wp = (uint8_t) ((dev->sh_wp >> 13) & 0x01); uint32_t base = ((uint32_t) (dev->hmwpb & 0x00f0)) << 17; uint32_t size = 0x01000000 - dev->hmwp_base; /* ACCESS_NORMAL means both ACCESS_BUS and ACCESS_CPU are set. */ mem_set_wp(dev->hmwp_base, size, ACCESS_NORMAL, 0); if (cpu_use_exec) wd76c10_recalc_exec(dev, dev->hmwp_base, size); size = 0x01000000 - base; mem_set_wp(base, size, ACCESS_NORMAL, hm_wp); if (cpu_use_exec) wd76c10_recalc_exec(dev, base, size); dev->hmwp_base = base; } static void wd76c10_pf_loc_reset(wd76c10_t *dev) { uint32_t base; for (uint8_t i = 0x031; i <= 0x03b; i++) { dev->mem_pages[i] = 0xff; base = ((uint32_t) i) << 14; wd76c10_set_mem_state(dev, base, 0x00004000, MEM_READ_EXTANY | MEM_WRITE_EXTANY, 0); if (cpu_use_exec) wd76c10_recalc_exec(dev, base, 0x00004000); } /* Re-apply any ROMCS#, etc. flags. */ wd76c10_shadow_ram_recalc(dev); } static void wd76c10_pf_loc_recalc(wd76c10_t *dev) { uint8_t pf_loc = (uint8_t) ((dev->ems_ctl >> 13) & 0x03); uint8_t ems_en = (uint8_t) ((dev->ems_ctl >> 10) & 0x03); uint8_t ems_page; uint32_t base; for (uint8_t i = (0x031 + pf_loc); i <= (0x037 + pf_loc); i++) { ems_page = (i - 0x10) & 0xf7; dev->mem_pages[i] = ems_page; base = ((uint32_t) i) << 14; dev->ems_pages[ems_page].virt = base; if ((ems_en >= 0x02) && dev->ems_pages[ems_page].enabled) { wd76c10_set_mem_state(dev, dev->ems_pages[ems_page].virt, 0x00004000, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL, 1); if (cpu_use_exec) wd76c10_recalc_exec(dev, dev->ems_pages[ems_page].virt, 0x00004000); } } } static void wd76c10_low_pages_recalc(wd76c10_t *dev) { uint8_t ems_page; uint32_t base; for (uint8_t i = 0x008; i <= 0x027; i++) { ems_page = i & 0x1f; dev->mem_pages[i] = ems_page; base = ((uint32_t) i) << 14; dev->ems_pages[ems_page].virt = base; if (cpu_use_exec) wd76c10_recalc_exec(dev, dev->ems_pages[ems_page].virt, 0x00004000); } } static void wd76c10_ser_par_cs_recalc(wd76c10_t *dev) { /* Serial B */ serial_remove(dev->uart[1]); switch ((dev->ser_par_cs >> 1) & 0x07) { case 0x01: serial_setup(dev->uart[1], 0x3f8, 3); break; case 0x02: serial_setup(dev->uart[1], 0x2f8, 3); break; case 0x03: serial_setup(dev->uart[1], 0x3e8, 3); break; case 0x04: serial_setup(dev->uart[1], 0x2e8, 3); break; } /* Serial A */ serial_remove(dev->uart[0]); switch ((dev->ser_par_cs >> 5) & 0x07) { case 0x01: serial_setup(dev->uart[0], 0x3f8, 4); break; case 0x02: serial_setup(dev->uart[0], 0x2f8, 4); break; case 0x03: serial_setup(dev->uart[0], 0x3e8, 4); break; case 0x04: serial_setup(dev->uart[0], 0x2e8, 4); break; } /* LPT */ lpt1_remove(); switch ((dev->ser_par_cs >> 9) & 0x03) { case 1: lpt1_init(0x3bc); lpt1_irq(7); break; case 2: lpt1_init(0x378); lpt1_irq(7); break; case 3: lpt1_init(0x278); lpt1_irq(7); break; } } static void wd76c10_disk_cs_recalc(wd76c10_t *dev) { ide_pri_disable(); ide_set_base(0, (dev->rtc_disk_cs & 0x0010) ? 0x0170 : 0x01f0); ide_set_side(0, (dev->rtc_disk_cs & 0x0010) ? 0x0376 : 0x03f6); if (!(dev->rtc_disk_cs & 0x0002)) ide_pri_enable(); fdc_remove(dev->fdc); if (!(dev->rtc_disk_cs & 0x0001)) fdc_set_base(dev->fdc, (dev->rtc_disk_cs & 0x0010) ? FDC_SECONDARY_ADDR : FDC_PRIMARY_ADDR); } static void wd76c10_outb(uint16_t port, uint8_t val, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; uint8_t lk_psw = (uint8_t) ((dev->rtc_disk_cs >> 2) & 0x01); uint8_t valxor; switch (port) { case 0x0092: if ((lk_psw && (val & 0x08)) || ((dev->p92 & 0x08) && !(val & 0x08))) val = (val & 0xf7) | (dev->p92 & 0x08); valxor = (dev->p92 ^ val) & 0x08; dev->p92 = (val & 0x08) | 0xf7; if (valxor) nvr_lock_set(0x38, 0x08, (val & 0x08) ? 0x03 : 0x00, dev->nvr); break; } } static void wd76c10_outw(uint16_t port, uint16_t val, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; uint8_t inc = (uint8_t) ((dev->ems_ctl >> 15) & 0x01); uint8_t ems_en; if (!dev->locked || (port < 0x1072) || (port > 0xf872) || (port == 0xe072) || (port == 0xe872) || (port == 0xf073)) switch (port) { case 0x1072: dev->cpuclk = val; break; case 0x1872: dev->fpu_ctl = val; break; case 0x2072: dev->ser_par_cs = val; wd76c10_ser_par_cs_recalc(dev); break; case 0x2872: dev->rtc_disk_cs = val; wd76c10_disk_cs_recalc(dev); break; case 0x3072: dev->prog_cs = val; break; /* TODO: Log this to determine how the BIOS does bank sizing. */ case 0x3872: dev->mem_ctl = val; wd76c10_banks_recalc(dev); break; case 0x4072: dev->npmdmt = val; break; /* A17-A24 */ case 0x4872: dev->bank_base_regs[0] = val; wd76c10_banks_recalc(dev); break; /* A17-A24 */ case 0x5072: dev->bank_base_regs[1] = val; wd76c10_banks_recalc(dev); break; case 0x5872: dev->split_sa = val; wd76c10_banks_recalc(dev); wd76c10_split_recalc(dev); break; case 0x6072: dev->sh_wp = val; wd76c10_dis_mem_recalc(dev); wd76c10_pf_loc_reset(dev); wd76c10_pf_loc_recalc(dev); wd76c10_low_pages_recalc(dev); wd76c10_high_mem_wp_recalc(dev); break; case 0x6872: dev->ems_ctl = val; wd76c10_pf_loc_reset(dev); wd76c10_pf_loc_recalc(dev); wd76c10_low_pages_recalc(dev); break; case 0x8872: dev->pmc_in = val; break; case 0xc072: dev->hmwpb = val; wd76c10_high_mem_wp_recalc(dev); break; case 0xe072: dev->ems_pp = val; dev->ep = (val & 0x3f) % 40; break; case 0xe872: ems_en = (uint8_t) ((dev->ems_ctl >> 10) & 0x03); if (ems_en) { dev->ems_page_regs[dev->ep] = val; dev->ems_pages[dev->ep].phys = ((uint32_t) (val & 0x0fff)) << 14; if (dev->ep >= 32) { dev->ems_pages[dev->ep].enabled = !!(val & 0x8000); if (ems_en >= 0x02) { wd76c10_pf_loc_reset(dev); wd76c10_pf_loc_recalc(dev); } } else { dev->ems_pages[dev->ep].enabled = (ems_en == 0x03); if (ems_en == 0x03) wd76c10_low_pages_recalc(dev); } } if (inc) dev->ep = (dev->ep + 1) % 40; break; case 0xf073: dev->locked = ((val & 0x00ff) != 0x00da); break; case 0xf872: flushmmucache(); break; } } static uint8_t wd76c10_inb(uint16_t port, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; uint8_t ret = 0xff; switch (port) { case 0x0092: ret = (dev->p92 & 0x08) | 0xf7; break; } return ret; } static uint16_t wd76c10_inw(uint16_t port, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; uint8_t inc = (uint8_t) ((dev->ems_ctl >> 15) & 0x01); uint16_t ret = 0xffff; wd76c10_log("WD76C10: R dev->regs[%04x]\n", port); if (!dev->locked || (port < 0x1072) || (port > 0xf872) || (port == 0xe072) || (port == 0xe872) || (port == 0xf073)) switch (port) { case 0x1072: ret = dev->cpuclk; break; case 0x1872: ret = dev->fpu_ctl; break; case 0x2072: ret = dev->ser_par_cs; break; case 0x2872: ret = dev->rtc_disk_cs; break; case 0x3072: ret = dev->prog_cs; break; case 0x3872: ret = dev->mem_ctl; break; case 0x4072: ret = dev->npmdmt; break; case 0x4872: ret = dev->bank_base_regs[0]; break; case 0x5072: ret = dev->bank_base_regs[1]; break; case 0x5872: ret = dev->split_sa; break; case 0x6072: ret = dev->sh_wp; break; case 0x6872: ret = dev->ems_ctl; break; case 0x8872: ret = dev->pmc_in; break; case 0xb872: ret = dma[0].mode; ret |= (((uint16_t) dma[1].mode) << 8); break; case 0xc072: ret = dev->hmwpb; break; case 0xd072: ret = (serial_read(0x0002, dev->uart[0]) & 0xc0) << 8; ret |= (serial_read(0x0002, dev->uart[1]) & 0xc0) << 6; ret |= (lpt_read_port(0, 0x0002) & 0x0f) << 8; ret |= lpt_read_port(0, 0x0000); break; case 0xe072: ret = (dev->ems_pp & 0xffc0) | dev->ep; break; case 0xe872: ret = dev->ems_page_regs[dev->ep]; if (inc) dev->ep = (dev->ep + 1) % 40; break; case 0xfc72: ret = ((lpt_read_status(0) & 0x20) >> 2); ret |= (((uint16_t) dma_m) << 4); ret |= dev->toggle; dev->toggle ^= 0x8000; break; } return ret; } static void wd76c10_close(void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; free(dev); } static void wd76c10_reset(void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; dev->locked = 1; dev->toggle = 0; dev->p92 = 0xf7; dev->cpuclk = 0x1000; dev->fpu_ctl = 0x00ca; dev->mem_ctl = 0x0000; dev->bank_base_regs[0] = 0x0000; dev->bank_base_regs[1] = 0x0000; dev->split_sa = 0x0000; dev->sh_wp = 0x0000; dev->hmwpb = 0x0000; dev->npmdmt = 0x0000; dev->ems_ctl = 0x0000; dev->ems_pp = 0x0000; dev->ser_par_cs = 0x0000; dev->rtc_disk_cs = 0x0000; for (uint8_t i = 0; i < 40; i++) { dev->ems_page_regs[i] = 0x0000; dev->ems_pages[i].enabled = 0; dev->ems_pages[i].phys = 0x00000000; } nvr_lock_set(0x38, 0x08, 0x00, dev->nvr); wd76c10_banks_recalc(dev); wd76c10_split_recalc(dev); wd76c10_dis_mem_recalc(dev); wd76c10_high_mem_wp_recalc(dev); wd76c10_pf_loc_reset(dev); wd76c10_pf_loc_recalc(dev); wd76c10_low_pages_recalc(dev); wd76c10_ser_par_cs_recalc(dev); wd76c10_disk_cs_recalc(dev); } static void * wd76c10_init(const device_t *info) { wd76c10_t *dev = (wd76c10_t *) calloc(1, sizeof(wd76c10_t)); uint32_t total_mem = mem_size << 10; uint32_t accum_mem = 0x00000000; ram_bank_t *rb; /* Calculate the physical RAM banks. */ for (uint8_t i = 0; i < 4; i++) { rb = &(dev->ram_banks[i]); uint32_t size = 0x00000000; for (int8_t j = 3; j >= 0; j--) { uint32_t *bs = &(bank_sizes[j]); if (*bs <= total_mem) { size = *bs; break; } } if (size != 0x00000000) { rb->phys_addr = accum_mem; rb->phys_size = size; total_mem -= size; accum_mem += size; } } rb = &(dev->ram_banks[4]); rb->phys_addr = 0x000a0000; rb->phys_size = 0x00060000; memset(dev->mem_pages, 0xff, sizeof(dev->mem_pages)); for (uint8_t i = 0x008; i < 0x01f; i++) dev->mem_pages[i] = i; for (uint8_t i = 0x020; i < 0x027; i++) dev->mem_pages[i] = i - 0x20; device_add(&port_92_inv_device); dev->nvr = device_add(&amstrad_megapc_nvr_device); dev->uart[0] = device_add_inst(&ns16450_device, 1); dev->uart[1] = device_add_inst(&ns16450_device, 2); dev->fdc = device_add(&fdc_at_device); device_add(&ide_isa_device); wd76c10_reset(dev); /* Password Lock */ io_sethandler(0x0092, 1, wd76c10_inb, NULL, NULL, wd76c10_outb, NULL, NULL, dev); /* Clock Control */ io_sethandler(0x1072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* FPU Bus Timing & Power Down Control */ io_sethandler(0x1872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Refresh Control, Serial and Parallel Chip Selects */ io_sethandler(0x2072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* RTC, PVGA, 80287 Timing, and Disk Chip Selects */ io_sethandler(0x2872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Programmable Chip Select Address */ io_sethandler(0x3072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Memory Control */ io_sethandler(0x3872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Non-page Mode DRAM Memory Timing */ io_sethandler(0x4072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Bank 1 & 0 Start Address */ io_sethandler(0x4872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Bank 3 & 2 Start Address */ io_sethandler(0x5072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Split Address */ io_sethandler(0x5872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* RAM Shadow And Write Protect */ io_sethandler(0x6072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* EMS Control And Lower EMS Boundary */ io_sethandler(0x6872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* PMC Inputs */ io_sethandler(0x8872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* DMA Mode Shadow Register */ io_sethandler(0xb872, 1, NULL, wd76c10_inw, NULL, NULL, NULL, NULL, dev); /* High Memory Write Protect Boundry */ io_sethandler(0xc072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Shadow Register */ io_sethandler(0xd072, 1, NULL, wd76c10_inw, NULL, NULL, NULL, NULL, dev); /* EMS Page Register Pointer */ io_sethandler(0xe072, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* EMS Page Register */ io_sethandler(0xe872, 1, NULL, wd76c10_inw, NULL, NULL, wd76c10_outw, NULL, dev); /* Lock/Unlock Configuration */ io_sethandler(0xf073, 1, NULL, NULL, NULL, NULL, wd76c10_outw, NULL, dev); /* Cache Flush */ io_sethandler(0xf872, 1, NULL, NULL, NULL, NULL, wd76c10_outw, NULL, dev); /* Lock Status */ io_sethandler(0xfc72, 1, NULL, wd76c10_inw, NULL, NULL, NULL, NULL, dev); dma_ext_mode_init(); mem_mapping_add(&dev->ram_mapping, 0x00000000, (mem_size + 384) << 10, wd76c10_read_ram, wd76c10_read_ramw, NULL, wd76c10_write_ram, wd76c10_write_ramw, NULL, ram, MEM_MAPPING_INTERNAL, dev); mem_mapping_disable(&ram_low_mapping); mem_mapping_disable(&ram_mid_mapping); mem_mapping_disable(&ram_high_mapping); mem_mapping_enable(&dev->ram_mapping); memset(dev->ram_state, 0x00, sizeof(dev->ram_state)); return dev; } const device_t wd76c10_device = { .name = "Western Digital WD76C10", .internal_name = "wd76c10", .flags = 0, .local = 0, .init = wd76c10_init, .close = wd76c10_close, .reset = NULL, { .available = NULL }, .speed_changed = NULL, .force_redraw = NULL, .config = NULL };