/* * 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 VIA VT82C49X chipset. * * * * Authors: Tiseno100, * Miran Grca, * * Copyright 2020 Tiseno100. * Copyright 2020 Miran Grca. */ #include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include "cpu.h" #include <86box/io.h> #include <86box/device.h> #include <86box/mem.h> #include <86box/smram.h> #include <86box/pic.h> #include <86box/hdc.h> #include <86box/hdc_ide.h> #include <86box/port_92.h> #include <86box/chipset.h> typedef struct { uint8_t has_ide, index, regs[256]; smram_t *smram_smm, *smram_low, *smram_high; } vt82c49x_t; #ifdef ENABLE_VT82C49X_LOG int vt82c49x_do_log = ENABLE_VT82C49X_LOG; static void vt82c49x_log(const char *fmt, ...) { va_list ap; if (vt82c49x_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else #define vt82c49x_log(fmt, ...) #endif static void vt82c49x_recalc(vt82c49x_t *dev) { int i, relocate; uint8_t reg, bit; uint32_t base, state; uint32_t shadow_bitmap = 0x00000000; relocate = (dev->regs[0x33] >> 2) & 0x03; shadowbios = 0; shadowbios_write = 0; for (i = 0; i < 8; i++) { base = 0xc0000 + (i << 14); reg = 0x30 + (i >> 2); bit = (i & 3) << 1; if ((base >= 0xc0000) && (base <= 0xc7fff)) { if (dev->regs[0x40] & 0x80) state = MEM_WRITE_DISABLED; else if ((dev->regs[reg]) & (1 << bit)) state = MEM_WRITE_INTERNAL; else state = (dev->regs[0x33] & 0x40) ? MEM_WRITE_ROMCS : MEM_WRITE_EXTERNAL; if ((dev->regs[reg]) & (1 << (bit + 1))) state |= MEM_READ_INTERNAL; else state |= (dev->regs[0x33] & 0x40) ? MEM_READ_ROMCS : MEM_READ_EXTERNAL; } if ((base >= 0xc8000) && (base <= 0xcffff)) { if ((dev->regs[reg]) & (1 << bit)) state = MEM_WRITE_INTERNAL; else state = (dev->regs[0x33] & 0x80) ? MEM_WRITE_ROMCS : MEM_WRITE_EXTERNAL; if ((dev->regs[reg]) & (1 << (bit + 1))) state |= MEM_READ_INTERNAL; else state |= (dev->regs[0x33] & 0x80) ? MEM_READ_ROMCS : MEM_READ_EXTERNAL; } else { state = ((dev->regs[reg]) & (1 << bit)) ? MEM_WRITE_INTERNAL : MEM_WRITE_EXTANY; state |= ((dev->regs[reg]) & (1 << (bit + 1))) ? MEM_READ_INTERNAL : MEM_READ_EXTANY; } vt82c49x_log("(%02X=%02X, %i) Setting %08X-%08X to: write %sabled, read %sabled\n", reg, dev->regs[reg], bit, base, base + 0x3fff, ((dev->regs[reg]) & (1 << bit)) ? "en" : "dis", ((dev->regs[reg]) & (1 << (bit + 1))) ? "en" : "dis"); if ((dev->regs[reg]) & (1 << bit)) shadow_bitmap |= (1 << i); if ((dev->regs[reg]) & (1 << (bit + 1))) shadow_bitmap |= (1 << (i + 16)); mem_set_mem_state_both(base, 0x4000, state); } for (i = 0; i < 4; i++) { base = 0xe0000 + (i << 15); bit = 6 - (i & 2); if ((base >= 0xe0000) && (base <= 0xe7fff)) { if (dev->regs[0x40] & 0x20) state = MEM_WRITE_DISABLED; else if ((dev->regs[0x32]) & (1 << bit)) state = MEM_WRITE_INTERNAL; else state = (dev->regs[0x33] & 0x10) ? MEM_WRITE_ROMCS : MEM_WRITE_EXTERNAL; if ((dev->regs[0x32]) & (1 << (bit + 1))) state |= MEM_READ_INTERNAL; else state |= (dev->regs[0x33] & 0x10) ? MEM_READ_ROMCS : MEM_READ_EXTERNAL; } else if ((base >= 0xe8000) && (base <= 0xeffff)) { if (dev->regs[0x40] & 0x20) state = MEM_WRITE_DISABLED; else if ((dev->regs[0x32]) & (1 << bit)) state = MEM_WRITE_INTERNAL; else state = (dev->regs[0x33] & 0x20) ? MEM_WRITE_ROMCS : MEM_WRITE_EXTERNAL; if ((dev->regs[0x32]) & (1 << (bit + 1))) state |= MEM_READ_INTERNAL; else state |= (dev->regs[0x33] & 0x20) ? MEM_READ_ROMCS : MEM_READ_EXTERNAL; } else { if (dev->regs[0x40] & 0x40) state = MEM_WRITE_DISABLED; else if ((dev->regs[0x32]) & (1 << bit)) state = ((dev->regs[0x32]) & (1 << bit)) ? MEM_WRITE_INTERNAL : MEM_WRITE_EXTANY; state |= ((dev->regs[0x32]) & (1 << (bit + 1))) ? MEM_READ_INTERNAL : MEM_READ_EXTANY; } vt82c49x_log("(32=%02X, %i) Setting %08X-%08X to: write %sabled, read %sabled\n", dev->regs[0x32], bit, base, base + 0x7fff, ((dev->regs[0x32]) & (1 << bit)) ? "en" : "dis", ((dev->regs[0x32]) & (1 << (bit + 1))) ? "en" : "dis"); if ((dev->regs[0x32]) & (1 << bit)) { shadow_bitmap |= (0xf << ((i << 2) + 8)); shadowbios_write |= 1; } if ((dev->regs[0x32]) & (1 << (bit + 1))) { shadow_bitmap |= (0xf << ((i << 2) + 24)); shadowbios |= 1; } mem_set_mem_state_both(base, 0x8000, state); } vt82c49x_log("Shadow bitmap: %08X\n", shadow_bitmap); mem_remap_top(0); switch (relocate) { case 0x02: if (!(shadow_bitmap & 0xfff0fff0)) mem_remap_top(256); break; case 0x03: if (!shadow_bitmap) mem_remap_top(384); break; } } static void vt82c49x_write(uint16_t addr, uint8_t val, void *priv) { vt82c49x_t *dev = (vt82c49x_t *) priv; uint8_t valxor; switch (addr) { case 0xa8: dev->index = val; break; case 0xa9: valxor = (val ^ dev->regs[dev->index]); if (dev->index == 0x55) dev->regs[dev->index] &= ~val; else dev->regs[dev->index] = val; vt82c49x_log("dev->regs[0x%02x] = %02x\n", dev->index, val); switch(dev->index) { /* Wait States */ case 0x03: cpu_update_waitstates(); break; /* Shadow RAM and top of RAM relocation */ case 0x30: case 0x31: case 0x32: case 0x33: case 0x40: vt82c49x_recalc(dev); break; /* External Cache Enable(Based on the 486-VC-HD BIOS) */ case 0x50: cpu_cache_ext_enabled = (val & 0x84); break; /* Software SMI */ case 0x54: if ((dev->regs[0x5b] & 0x80) && (valxor & 0x01) && (val & 0x01)) { if (dev->regs[0x5b] & 0x20) smi_line = 1; else picint(1 << 15); dev->regs[0x55] = 0x01; } break; /* SMRAM */ case 0x5b: smram_disable_all(); if (val & 0x80) { smram_enable(dev->smram_smm, (val & 0x40) ? 0x00060000 : 0x00030000, 0x000a0000, 0x00020000, 0, (val & 0x10)); smram_enable(dev->smram_high, 0x000a0000, 0x000a0000, 0x00020000, (val & 0x08), (val & 0x08)); smram_enable(dev->smram_low, 0x00030000, 0x000a0000, 0x00020000, (val & 0x02), 0); } break; /* Edge/Level IRQ Control */ case 0x62: case 0x63: if (dev->index == 0x63) pic_elcr_write(dev->index, val & 0xde, &pic2); else { pic_elcr_write(dev->index, val & 0xf8, &pic); pic_elcr_set_enabled(val & 0x01); } break; /* Local Bus IDE Controller */ case 0x71: if (dev->has_ide) { ide_pri_disable(); ide_set_base(0, (val & 0x40) ? 0x170 : 0x1f0); ide_set_side(0, (val & 0x40) ? 0x376 : 0x3f6); if (val & 0x01) ide_pri_enable(); vt82c49x_log("VT82C496 IDE now %sabled as %sary\n", (val & 0x01) ? "en": "dis", (val & 0x40) ? "second" : "prim"); } break; } break; } } static uint8_t vt82c49x_read(uint16_t addr, void *priv) { uint8_t ret = 0xff; vt82c49x_t *dev = (vt82c49x_t *) priv; switch (addr) { case 0xa9: /* Register 64h is jumper readout. */ if (dev->index == 0x64) ret = 0xff; else if (dev->index == 0x63) ret = pic_elcr_read(dev->index, &pic2) | (dev->regs[dev->index] & 0x01); else if (dev->index == 0x62) ret = pic_elcr_read(dev->index, &pic) | (dev->regs[dev->index] & 0x07); else if (dev->index < 0x80) ret = dev->regs[dev->index]; break; } return ret; } static void vt82c49x_reset(void *priv) { uint16_t i; for (i = 0; i < 256; i++) vt82c49x_write(i, 0x00, priv); } static void vt82c49x_close(void *priv) { vt82c49x_t *dev = (vt82c49x_t *) priv; smram_del(dev->smram_high); smram_del(dev->smram_low); smram_del(dev->smram_smm); free(dev); } static void * vt82c49x_init(const device_t *info) { vt82c49x_t *dev = (vt82c49x_t *) malloc(sizeof(vt82c49x_t)); memset(dev, 0x00, sizeof(vt82c49x_t)); dev->smram_smm = smram_add(); dev->smram_low = smram_add(); dev->smram_high = smram_add(); dev->has_ide = info->local & 1; if (dev->has_ide) { device_add(&ide_vlb_2ch_device); ide_sec_disable(); } device_add(&port_92_device); io_sethandler(0x0a8, 0x0002, vt82c49x_read, NULL, NULL, vt82c49x_write, NULL, NULL, dev); pic_elcr_io_handler(0); pic_elcr_set_enabled(1); vt82c49x_recalc(dev); return dev; } const device_t via_vt82c49x_device = { "VIA VT82C49X", "via_vt82c49x", 0, 0, vt82c49x_init, vt82c49x_close, NULL, { NULL }, NULL, NULL, NULL }; const device_t via_vt82c49x_pci_device = { "VIA VT82C49X PCI", "via_vt82c49x_pci", DEVICE_PCI, 0, vt82c49x_init, vt82c49x_close, vt82c49x_reset, { NULL }, NULL, NULL, NULL }; const device_t via_vt82c49x_ide_device = { "VIA VT82C49X (With IDE)", "via_vt82c49x_ide", 0, 1, vt82c49x_init, vt82c49x_close, NULL, { NULL }, NULL, NULL, NULL }; const device_t via_vt82c49x_pci_ide_device = { "VIA VT82C49X PCI (With IDE)", "via_vt82c49x_pci_ide", DEVICE_PCI, 1, vt82c49x_init, vt82c49x_close, vt82c49x_reset, { NULL }, NULL, NULL, NULL };