/* * 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 SiS 85c401/85c402, 85c460, 85c461, and * 85c407/85c471 chipsets. * * Authors: Miran Grca, * * Copyright 2019,2020 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/keyboard.h> #include <86box/timer.h> #include <86box/port_92.h> #include <86box/mem.h> #include <86box/smram.h> #include <86box/fdd.h> #include <86box/fdc.h> #include <86box/pic.h> #include <86box/machine.h> #include <86box/chipset.h> typedef struct { uint8_t cur_reg, tries, reg_base, reg_last, is_471, regs[39], scratch[2]; smram_t *smram; port_92_t *port_92; } sis_85c4xx_t; static void sis_85c4xx_recalcmapping(sis_85c4xx_t *dev) { uint32_t base; uint32_t i, shflags = 0; uint32_t readext, writeext; uint8_t romcs = 0xc0, cur_romcs; shadowbios = 0; shadowbios_write = 0; if (dev->regs[0x03] & 0x40) romcs |= 0x01; if (dev->regs[0x03] & 0x80) romcs |= 0x30; if (dev->regs[0x08] & 0x04) romcs |= 0x02; for (i = 0; i < 8; i++) { base = 0xc0000 + (i << 15); cur_romcs = romcs & (1 << i); readext = cur_romcs ? MEM_READ_EXTANY : MEM_READ_EXTERNAL; writeext = cur_romcs ? MEM_WRITE_EXTANY : MEM_WRITE_EXTERNAL; if ((i > 5) || (dev->regs[0x02] & (1 << i))) { shadowbios |= (base >= 0xe0000) && (dev->regs[0x02] & 0x80); shadowbios_write |= (base >= 0xe0000) && !(dev->regs[0x02] & 0x40); shflags = (dev->regs[0x02] & 0x80) ? MEM_READ_INTERNAL : readext; shflags |= (dev->regs[0x02] & 0x40) ? writeext : MEM_WRITE_INTERNAL; mem_set_mem_state(base, 0x8000, shflags); } else mem_set_mem_state(base, 0x8000, readext | writeext); } flushmmucache(); } static void sis_85c4xx_sw_smi_out(uint16_t port, uint8_t val, void *priv) { sis_85c4xx_t *dev = (sis_85c4xx_t *) priv; if (dev->regs[0x18] & 0x02) { if (dev->regs[0x0b] & 0x10) smi_line = 1; else picint(1 << ((dev->regs[0x0b] & 0x08) ? 15 : 12)); soft_reset_mask = 1; dev->regs[0x19] |= 0x02; } } static void sis_85c4xx_sw_smi_handler(sis_85c4xx_t *dev) { uint16_t addr; if (!dev->is_471) return; addr = dev->regs[0x14] | (dev->regs[0x15] << 8); io_handler((dev->regs[0x0b] & 0x80) && (dev->regs[0x18] & 0x02), addr, 0x0001, NULL, NULL, NULL, sis_85c4xx_sw_smi_out, NULL, NULL, dev); } static void sis_85c4xx_out(uint16_t port, uint8_t val, void *priv) { sis_85c4xx_t *dev = (sis_85c4xx_t *) priv; uint8_t rel_reg = dev->cur_reg - dev->reg_base; uint8_t valxor = 0x00; uint32_t host_base = 0x000e0000, ram_base = 0x000a0000; switch (port) { case 0x22: dev->cur_reg = val; break; case 0x23: if ((dev->cur_reg >= dev->reg_base) && (dev->cur_reg <= dev->reg_last)) { valxor = val ^ dev->regs[rel_reg]; if (rel_reg == 0x19) dev->regs[rel_reg] &= ~val; else dev->regs[rel_reg] = val; switch (rel_reg) { case 0x01: cpu_cache_ext_enabled = ((val & 0x84) == 0x84); cpu_update_waitstates(); break; case 0x02: case 0x03: case 0x08: sis_85c4xx_recalcmapping(dev); break; case 0x0b: sis_85c4xx_sw_smi_handler(dev); if (dev->is_471 && (valxor & 0x02)) { if (val & 0x02) mem_remap_top(0); else mem_remap_top(256); } break; case 0x13: if (dev->is_471 && (valxor & 0xf0)) { smram_disable(dev->smram); host_base = (val & 0x80) ? 0x00060000 : 0x000e0000; switch ((val >> 5) & 0x03) { case 0x00: ram_base = 0x000a0000; break; case 0x01: ram_base = 0x000b0000; break; case 0x02: ram_base = (val & 0x80) ? 0x00000000 : 0x000e0000; break; default: ram_base = 0x00000000; break; } if (ram_base != 0x00000000) smram_enable(dev->smram, host_base, ram_base, 0x00010000, (val & 0x10), 1); } break; case 0x14: case 0x15: case 0x18: sis_85c4xx_sw_smi_handler(dev); break; case 0x1c: if (dev->is_471) soft_reset_mask = 0; break; case 0x22: if (dev->is_471 && (valxor & 0x01)) { port_92_remove(dev->port_92); if (val & 0x01) port_92_add(dev->port_92); } break; } } dev->cur_reg = 0x00; break; case 0xe1: case 0xe2: dev->scratch[port - 0xe1] = val; return; } } static uint8_t sis_85c4xx_in(uint16_t port, void *priv) { sis_85c4xx_t *dev = (sis_85c4xx_t *) priv; uint8_t rel_reg = dev->cur_reg - dev->reg_base; uint8_t ret = 0xff; switch (port) { case 0x23: if (dev->is_471 && (dev->cur_reg == 0x1c)) ret = inb(0x70); if ((dev->cur_reg >= dev->reg_base) && (dev->cur_reg <= dev->reg_last)) ret = dev->regs[rel_reg]; dev->cur_reg = 0x00; break; case 0xe1: case 0xe2: ret = dev->scratch[port - 0xe1]; } return ret; } static void sis_85c4xx_close(void *priv) { sis_85c4xx_t *dev = (sis_85c4xx_t *) priv; if (dev->is_471) smram_del(dev->smram); free(dev); } static void * sis_85c4xx_init(const device_t *info) { int mem_size_mb; sis_85c4xx_t *dev = (sis_85c4xx_t *) malloc(sizeof(sis_85c4xx_t)); memset(dev, 0, sizeof(sis_85c4xx_t)); dev->is_471 = (info->local >> 8) & 0xff; dev->reg_base = info->local & 0xff; mem_size_mb = mem_size >> 10; if (cpu_s->rspeed < 25000000) dev->regs[0x08] = 0x80; if (dev->is_471) { dev->reg_last = dev->reg_base + 0x76; dev->regs[0x09] = 0x40; switch (mem_size_mb) { case 0: case 1: dev->regs[0x09] |= 0x00; break; case 2: case 3: dev->regs[0x09] |= 0x01; break; case 4: dev->regs[0x09] |= 0x02; break; case 5: dev->regs[0x09] |= 0x20; break; case 6: case 7: dev->regs[0x09] |= 0x09; break; case 8: case 9: dev->regs[0x09] |= 0x04; break; case 10: case 11: dev->regs[0x09] |= 0x05; break; case 12: case 13: case 14: case 15: dev->regs[0x09] |= 0x0b; break; case 16: dev->regs[0x09] |= 0x13; break; case 17: dev->regs[0x09] |= 0x21; break; case 18: case 19: dev->regs[0x09] |= 0x06; break; case 20: case 21: case 22: case 23: dev->regs[0x09] |= 0x0d; break; case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: dev->regs[0x09] |= 0x0e; break; case 32: case 33: case 34: case 35: dev->regs[0x09] |= 0x1b; break; case 36: case 37: case 38: case 39: dev->regs[0x09] |= 0x0f; break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: dev->regs[0x09] |= 0x17; break; case 48: dev->regs[0x09] |= 0x1e; break; default: if (mem_size_mb < 64) dev->regs[0x09] |= 0x1e; else if ((mem_size_mb >= 65) && (mem_size_mb < 68)) dev->regs[0x09] |= 0x22; else dev->regs[0x09] |= 0x24; break; } dev->regs[0x11] = 0x09; dev->regs[0x12] = 0xff; dev->regs[0x1f] = 0x20; /* Video access enabled. */ dev->regs[0x23] = 0xf0; dev->regs[0x26] = 0x01; dev->smram = smram_add(); smram_enable(dev->smram, 0x000e0000, 0x000a0000, 0x00010000, 0, 1); dev->port_92 = device_add(&port_92_device); port_92_remove(dev->port_92); } else { dev->reg_last = dev->reg_base + 0x11; switch (mem_size_mb) { case 1: default: dev->regs[0x00] = 0x00; break; case 2: dev->regs[0x00] = 0x01; break; case 4: dev->regs[0x00] = 0x02; break; case 6: dev->regs[0x00] = 0x03; break; case 8: dev->regs[0x00] = 0x04; break; case 10: dev->regs[0x00] = 0x05; break; case 12: dev->regs[0x00] = 0x0b; break; case 16: dev->regs[0x00] = 0x19; break; case 18: dev->regs[0x00] = 0x06; break; case 20: dev->regs[0x00] = 0x14; break; case 24: dev->regs[0x00] = 0x15; break; case 32: dev->regs[0x00] = 0x1b; break; case 36: dev->regs[0x00] = 0x16; break; case 40: dev->regs[0x00] = 0x17; break; case 48: dev->regs[0x00] = 0x1e; break; case 64: dev->regs[0x00] = 0x1f; break; } dev->regs[0x11] = 0x01; } io_sethandler(0x0022, 0x0002, sis_85c4xx_in, NULL, NULL, sis_85c4xx_out, NULL, NULL, dev); dev->scratch[0] = dev->scratch[1] = 0xff; io_sethandler(0x00e1, 0x0002, sis_85c4xx_in, NULL, NULL, sis_85c4xx_out, NULL, NULL, dev); sis_85c4xx_recalcmapping(dev); return dev; } const device_t sis_85c401_device = { "SiS 85c401/85c402", 0, 0x060, sis_85c4xx_init, sis_85c4xx_close, NULL, { NULL }, NULL, NULL, NULL }; const device_t sis_85c460_device = { "SiS 85c460", 0, 0x050, sis_85c4xx_init, sis_85c4xx_close, NULL, { NULL }, NULL, NULL, NULL }; /* TODO: Log to make sure the registers are correct. */ const device_t sis_85c461_device = { "SiS 85c461", 0, 0x050, sis_85c4xx_init, sis_85c4xx_close, NULL, { NULL }, NULL, NULL, NULL }; const device_t sis_85c471_device = { "SiS 85c407/85c471", 0, 0x150, sis_85c4xx_init, sis_85c4xx_close, NULL, { NULL }, NULL, NULL, NULL };