/* * 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 #include #define HAVE_STDARG_H #include <86box/86box.h> #include "cpu.h" #include "x86.h" #include <86box/timer.h> #include <86box/io.h> #include <86box/device.h> #include <86box/plat_unused.h> #include <86box/port_92.h> #include <86box/mem.h> #include <86box/smram.h> #include <86box/pic.h> #include <86box/plat_fallthrough.h> #include <86box/keyboard.h> #include <86box/machine.h> #include <86box/chipset.h> typedef struct ram_bank_t { uint32_t virt_base; uint32_t virt_size; uint32_t phys_base; uint32_t phys_size; mem_mapping_t mapping; } ram_bank_t; typedef struct sis_85c4xx_t { uint8_t cur_reg; uint8_t tries; uint8_t reg_base; uint8_t reg_last; uint8_t reg_00; uint8_t is_471; uint8_t ram_banks_val; uint8_t force_flush; uint8_t shadowed; uint8_t smram_enabled; uint8_t pad; uint8_t regs[39]; uint8_t scratch[2]; uint32_t mem_state[8]; ram_bank_t ram_banks[8]; smram_t * smram; port_92_t * port_92; } sis_85c4xx_t; static uint8_t ram_4xx[64] = { 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x19, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t ram_471[64] = { 0x00, 0x00, 0x01, 0x01, 0x02, 0x20, 0x09, 0x09, 0x04, 0x04, 0x05, 0x05, 0x0b, 0x0b, 0x0b, 0x0b, 0x13, 0x21, 0x06, 0x06, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x1b, 0x1b, 0x1b, 0x1b, 0x0f, 0x0f, 0x0f, 0x0f, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d }; static uint8_t ram_asus[64] = { 0x00, 0x00, 0x01, 0x10, 0x10, 0x20, 0x03, 0x11, 0x11, 0x05, 0x05, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x21, 0x06, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f }; static uint8_t ram_tg486g[64] = { 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f }; static uint32_t banks_471[64][4] = { { 0x00100000, 0x00000000, 0x00000000, 0x00000000 }, /* 0x00 */ { 0x00100000, 0x00100000, 0x00000000, 0x00000000 }, { 0x00100000, 0x00100000, 0x00200000, 0x00000000 }, { 0x00100000, 0x00100000, 0x00400000, 0x00000000 }, { 0x00100000, 0x00100000, 0x00200000, 0x00400000 }, { 0x00100000, 0x00100000, 0x00400000, 0x00400000 }, { 0x00100000, 0x00100000, 0x01000000, 0x00000000 }, { 0x00200000, 0x00000000, 0x00000000, 0x00000000 }, { 0x00200000, 0x00200000, 0x00000000, 0x00000000 }, /* 0x08 */ { 0x00200000, 0x00400000, 0x00000000, 0x00000000 }, { 0x00200000, 0x00200000, 0x00400000, 0x00000000 }, { 0x00200000, 0x00200000, 0x00400000, 0x00400000 }, { 0x00200000, 0x01000000, 0x00000000, 0x00000000 }, { 0x00200000, 0x00200000, 0x01000000, 0x00000000 }, { 0x00200000, 0x00200000, 0x00400000, 0x01000000 }, { 0x00200000, 0x00200000, 0x01000000, 0x01000000 }, { 0x00400000, 0x00000000, 0x00000000, 0x00000000 }, /* 0x10 */ { 0x00400000, 0x00400000, 0x00000000, 0x00000000 }, { 0x00400000, 0x00400000, 0x00400000, 0x00000000 }, { 0x00400000, 0x00400000, 0x00400000, 0x00400000 }, { 0x00400000, 0x01000000, 0x00000000, 0x00000000 }, { 0x00400000, 0x00400000, 0x01000000, 0x00000000 }, { 0x00400000, 0x01000000, 0x01000000, 0x00000000 }, { 0x00400000, 0x00400000, 0x01000000, 0x01000000 }, { 0x00800000, 0x00000000, 0x00000000, 0x00000000 }, /* 0x18 */ { 0x00800000, 0x00800000, 0x00000000, 0x00000000 }, { 0x00800000, 0x00800000, 0x00800000, 0x00000000 }, { 0x00800000, 0x00800000, 0x00800000, 0x00800000 }, { 0x01000000, 0x00000000, 0x00000000, 0x00000000 }, { 0x01000000, 0x01000000, 0x00000000, 0x00000000 }, { 0x01000000, 0x01000000, 0x01000000, 0x00000000 }, { 0x01000000, 0x01000000, 0x01000000, 0x01000000 }, { 0x00100000, 0x00400000, 0x00000000, 0x00000000 }, /* 0x20 */ { 0x00100000, 0x01000000, 0x00000000, 0x00000000 }, { 0x00100000, 0x04000000, 0x00000000, 0x00000000 }, { 0x00400000, 0x00800000, 0x00000000, 0x00000000 }, { 0x00400000, 0x04000000, 0x00000000, 0x00000000 }, { 0x00400000, 0x00400000, 0x04000000, 0x00000000 }, { 0x01000000, 0x04000000, 0x00000000, 0x00000000 }, { 0x01000000, 0x01000000, 0x04000000, 0x00000000 }, { 0x04000000, 0x00000000, 0x00000000, 0x00000000 }, /* 0x28 */ { 0x04000000, 0x04000000, 0x00000000, 0x00000000 }, { 0x00400000, 0x02000000, 0x00000000, 0x00000000 }, { 0x00400000, 0x02000000, 0x02000000, 0x00000000 }, { 0x00400000, 0x00400000, 0x02000000, 0x00000000 }, { 0x00400000, 0x00400000, 0x02000000, 0x02000000 }, { 0x01000000, 0x02000000, 0x00000000, 0x00000000 }, { 0x01000000, 0x02000000, 0x02000000, 0x00000000 }, { 0x01000000, 0x01000000, 0x02000000, 0x00000000 }, /* 0x30 */ { 0x01000000, 0x01000000, 0x02000000, 0x02000000 }, { 0x02000000, 0x00000000, 0x00000000, 0x00000000 }, { 0x02000000, 0x02000000, 0x00000000, 0x00000000 }, { 0x02000000, 0x02000000, 0x02000000, 0x00000000 }, { 0x02000000, 0x02000000, 0x02000000, 0x02000000 }, { 0x00400000, 0x00800000, 0x00800000, 0x00000000 }, { 0x00400000, 0x00800000, 0x00800000, 0x00800000 }, { 0x00400000, 0x00400000, 0x00800000, 0x00000000 }, /* 0x38 */ { 0x00400000, 0x00400000, 0x00800000, 0x00800000 }, { 0x00800000, 0x01000000, 0x00000000, 0x00000000 }, { 0x00800000, 0x00800000, 0x00800000, 0x01000000 }, { 0x00800000, 0x00800000, 0x01000000, 0x00000000 }, { 0x00800000, 0x00800000, 0x01000000, 0x01000000 }, { 0x00800000, 0x00800000, 0x02000000, 0x00000000 }, { 0x00800000, 0x00800000, 0x02000000, 0x02000000 } }; static uint32_t sis_85c471_get_row(ram_bank_t *dev, uint32_t addr) { uint32_t ret = 0x00000000; switch (dev->virt_size) { case 0x00100000: case 0x00200000: ret |= (addr >> 13) & 0x00000001; ret |= ((addr >> 12) & 0x00000001) << 1; ret |= ((addr >> 14) & 0x0000003f) << 2; ret |= ((addr >> 11) & 0x00000001) << 8; ret |= ((addr >> 20) & 0x00000001) << 9; ret |= ((addr >> 22) & 0x00000001) << 10; ret |= ((addr >> 24) & 0x00000001) << 11; break; case 0x00400000: case 0x00800000: ret |= (addr >> 13) & 0x00000001; ret |= ((addr >> 12) & 0x00000001) << 1; ret |= ((addr >> 14) & 0x000000ff) << 2; ret |= ((addr >> 22) & 0x00000001) << 10; ret |= ((addr >> 24) & 0x00000001) << 11; break; case 0x01000000: case 0x02000000: case 0x04000000: ret |= (addr >> 13) & 0x00000001; ret |= ((addr >> 22) & 0x00000001) << 1; ret |= ((addr >> 14) & 0x000000ff) << 2; ret |= ((addr >> 23) & 0x00000001) << 10; ret |= ((addr >> 24) & 0x00000001) << 11; break; } return ret; } static uint32_t sis_85c471_get_col(ram_bank_t *dev, uint32_t addr) { uint32_t ret = 0x00000000; switch (dev->virt_size) { case 0x00100000: case 0x00200000: ret |= (addr >> 3) & 0x00000001; ret |= ((addr >> 2) & 0x00000001) << 1; ret |= ((addr >> 4) & 0x0000003f) << 2; ret |= ((addr >> 10) & 0x00000001) << 8; ret |= ((addr >> 21) & 0x00000001) << 9; ret |= ((addr >> 23) & 0x00000001) << 10; ret |= ((addr >> 25) & 0x00000001) << 11; break; case 0x00400000: case 0x00800000: ret |= (addr >> 3) & 0x00000001; ret |= ((addr >> 2) & 0x00000001) << 1; ret |= ((addr >> 4) & 0x000000ff) << 2; ret |= ((addr >> 23) & 0x00000001) << 10; ret |= ((addr >> 25) & 0x00000001) << 11; break; case 0x01000000: case 0x02000000: case 0x04000000: ret |= (addr >> 3) & 0x00000001; ret |= ((addr >> 2) & 0x00000001) << 1; ret |= ((addr >> 4) & 0x000001ff) << 2; ret |= ((addr >> 25) & 0x00000001) << 11; break; } return ret; } static uint32_t sis_85c471_set_row(ram_bank_t *dev, uint32_t addr) { uint32_t ret = 0x00000000; switch (dev->phys_size) { case 0x00100000: ret = (addr & 0x1ff) << 11; break; case 0x00200000: ret = (addr & 0x3ff) << 11; break; case 0x00400000: ret = (addr & 0x3ff) << 12; break; case 0x00800000: ret = (addr & 0x7ff) << 12; break; case 0x01000000: ret = (addr & 0x7ff) << 13; break; case 0x02000000: ret = (addr & 0xfff) << 13; break; case 0x04000000: ret = (addr & 0xfff) << 14; break; } return ret; } static uint32_t sis_85c471_set_col(ram_bank_t *dev, uint32_t addr) { uint32_t ret = 0x00000000; switch (dev->phys_size) { case 0x00100000: case 0x00200000: ret = (addr & 0x1ff) << 2; break; case 0x00400000: case 0x00800000: ret = (addr & 0x3ff) << 2; break; case 0x01000000: case 0x02000000: ret = (addr & 0x7ff) << 2; break; case 0x04000000: ret = (addr & 0xfff) << 2; break; } return ret; } uint8_t reg09 = 0x00; static uint8_t sis_85c471_read_ram(uint32_t addr, void *priv) { ram_bank_t *dev = (ram_bank_t *) priv; uint32_t rel = addr - dev->virt_base; uint8_t ret = 0xff; uint32_t row = sis_85c471_set_row(dev, sis_85c471_get_row(dev, rel)); uint32_t col = sis_85c471_set_col(dev, sis_85c471_get_col(dev, rel)); uint32_t dw = rel & 0x00000003; rel = row | col | dw; addr = (rel + dev->phys_base); if ((addr < (mem_size << 10)) && (rel < dev->phys_size)) ret = ram[addr]; return ret; } static uint16_t sis_85c471_read_ramw(uint32_t addr, void *priv) { ram_bank_t *dev = (ram_bank_t *) priv; uint32_t rel = addr - dev->virt_base; uint16_t ret = 0xffff; uint32_t row = sis_85c471_set_row(dev, sis_85c471_get_row(dev, rel)); uint32_t col = sis_85c471_set_col(dev, sis_85c471_get_col(dev, rel)); uint32_t dw = rel & 0x00000003; rel = row | col | dw; addr = (rel + dev->phys_base); if ((addr < (mem_size << 10)) && (rel < dev->phys_size)) ret = *(uint16_t *) &(ram[addr]); return ret; } static uint32_t sis_85c471_read_raml(uint32_t addr, void *priv) { ram_bank_t *dev = (ram_bank_t *) priv; uint32_t rel = addr - dev->virt_base; uint32_t ret = 0xffffffff; uint32_t row = sis_85c471_set_row(dev, sis_85c471_get_row(dev, rel)); uint32_t col = sis_85c471_set_col(dev, sis_85c471_get_col(dev, rel)); uint32_t dw = rel & 0x00000003; rel = row | col | dw; addr = (rel + dev->phys_base); if ((addr < (mem_size << 10)) && (rel < dev->phys_size)) ret = *(uint32_t *) &(ram[addr]); return ret; } static void sis_85c471_write_ram(uint32_t addr, uint8_t val, void *priv) { ram_bank_t *dev = (ram_bank_t *) priv; uint32_t rel = addr - dev->virt_base; uint32_t row = sis_85c471_set_row(dev, sis_85c471_get_row(dev, rel)); uint32_t col = sis_85c471_set_col(dev, sis_85c471_get_col(dev, rel)); uint32_t dw = rel & 0x00000003; rel = row | col | dw; addr = (rel + dev->phys_base); if ((addr < (mem_size << 10)) && (rel < dev->phys_size)) ram[addr] = val; } static void sis_85c471_write_ramw(uint32_t addr, uint16_t val, void *priv) { ram_bank_t *dev = (ram_bank_t *) priv; uint32_t rel = addr - dev->virt_base; uint32_t row = sis_85c471_set_row(dev, sis_85c471_get_row(dev, rel)); uint32_t col = sis_85c471_set_col(dev, sis_85c471_get_col(dev, rel)); uint32_t dw = rel & 0x00000003; rel = row | col | dw; addr = (rel + dev->phys_base); if ((addr < (mem_size << 10)) && (rel < dev->phys_size)) *(uint16_t *) &(ram[addr]) = val; } static void sis_85c471_write_raml(uint32_t addr, uint32_t val, void *priv) { ram_bank_t *dev = (ram_bank_t *) priv; uint32_t rel = addr - dev->virt_base; uint32_t row = sis_85c471_set_row(dev, sis_85c471_get_row(dev, rel)); uint32_t col = sis_85c471_set_col(dev, sis_85c471_get_col(dev, rel)); uint32_t dw = rel & 0x00000003; rel = row | col | dw; addr = (rel + dev->phys_base); if ((addr < (mem_size << 10)) && (rel < dev->phys_size)) *(uint32_t *) &(ram[addr]) = val; } static void sis_85c4xx_recalcremap(sis_85c4xx_t *dev) { if (dev->is_471) { if ((mem_size > 8192) || (dev->shadowed & 0x3c) || (dev->regs[0x0b] & 0x02)) mem_remap_top(0); else mem_remap_top(-256); } } static void sis_85c4xx_recalcmapping(sis_85c4xx_t *dev) { uint32_t base; uint32_t n = 0; uint32_t shflags = 0; uint32_t readext; uint32_t writeext; uint8_t romcs = 0xc0; uint8_t cur_romcs; dev->shadowed = 0x00; 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 (uint8_t 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; if (dev->regs[0x02] & 0x80) dev->shadowed |= (1 << i); if (!(dev->regs[0x02] & 0x40)) dev->shadowed |= (1 << i); if (dev->force_flush || (dev->mem_state[i] != shflags)) { n++; mem_set_mem_state_both(base, 0x8000, shflags); if ((base >= 0xf0000) && (dev->mem_state[i] & MEM_READ_INTERNAL) && !(shflags & MEM_READ_INTERNAL)) mem_invalidate_range(base, base + 0x7fff); dev->mem_state[i] = shflags; } } else { shflags = readext | writeext; if (dev->force_flush || (dev->mem_state[i] != shflags)) { n++; mem_set_mem_state_both(base, 0x8000, shflags); dev->mem_state[i] = shflags; } } } if (dev->force_flush) { flushmmucache(); dev->force_flush = 0; } else if (n > 0) flushmmucache_nopc(); sis_85c4xx_recalcremap(dev); } static void sis_85c4xx_sw_smi_out(UNUSED(uint16_t port), UNUSED(uint8_t val), void *priv) { sis_85c4xx_t *dev = (sis_85c4xx_t *) priv; if (dev->regs[0x18] & 0x02) { if (dev->regs[0x0b] & 0x10) smi_raise(); 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_85c471_banks_split(uint32_t *b_ex, uint32_t *banks) { for (uint8_t i = 0; i < 4; i++) { if ((banks[i] == 0x00200000) || (banks[i] == 0x00800000) || (banks[i] == 0x02000000)) b_ex[i << 1] = b_ex[(i << 1) + 1] = banks[i] >> 1; else { b_ex[i << 1] = banks[i]; b_ex[(i << 1) + 1] = 0x00000000; } } } static void sis_85c471_banks_recalc(sis_85c4xx_t *dev) { reg09 = dev->regs[0x09]; for (uint8_t i = 0; i < 8; i++) mem_mapping_disable(&dev->ram_banks[i].mapping); mem_mapping_disable(&ram_low_mapping); mem_mapping_disable(&ram_high_mapping); mem_set_mem_state_both(1 << 20, 127 << 20, MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL); if ((dev->regs[0x09] & 0x3f) == dev->ram_banks_val) { if (mem_size > 1024) { mem_mapping_enable(&ram_low_mapping); mem_mapping_enable(&ram_high_mapping); mem_set_mem_state_both(1 << 20, (mem_size << 10) - (1 << 20), MEM_READ_INTERNAL | MEM_WRITE_INTERNAL); } } else { uint8_t banks_val = dev->regs[0x09] & 0x3f; uint32_t *banks = banks_471[banks_val]; uint32_t b_ex[8] = { 0x00000000 }; uint32_t size = 0x00000000; sis_85c471_banks_split(b_ex, banks); for (uint8_t i = 0; i < 8; i++) if (b_ex[i] != 0x00000000) { dev->ram_banks[i].virt_base = size; dev->ram_banks[i].virt_size = b_ex[i]; mem_mapping_set_addr(&dev->ram_banks[i].mapping, size, b_ex[i]); size += b_ex[i]; } mem_set_mem_state_both(1 << 20, 127 << 20, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL); } flushmmucache_nopc(); } 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; uint32_t 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 (!dev->is_471 && (rel_reg == 0x00)) dev->regs[rel_reg] = (dev->regs[rel_reg] & 0x1f) | (val & 0xe0); else dev->regs[rel_reg] = val; switch (rel_reg) { case 0x00: break; case 0x01: cpu_cache_ext_enabled = ((val & 0x84) == 0x84); cpu_update_waitstates(); break; case 0x02: case 0x03: case 0x08: if (valxor) sis_85c4xx_recalcmapping(dev); if ((rel_reg == 0x08) && dev->is_471) flushmmucache(); break; case 0x09: if (dev->is_471) sis_85c471_banks_recalc(dev); break; case 0x0b: sis_85c4xx_sw_smi_handler(dev); if (valxor & 0x02) sis_85c4xx_recalcremap(dev); break; case 0x10: if (dev->reg_base == 0x50) { double bus_clk; switch (val & 0xe0) { default: case 0x00: bus_clk = 7159091.0; break; case 0x02: bus_clk = cpu_busspeed / 10.0; break; case 0x04: bus_clk = cpu_busspeed / 8.0; break; case 0x06: bus_clk = cpu_busspeed / 6.0; break; case 0x80: bus_clk = cpu_busspeed / 5.0; break; case 0xa0: bus_clk = cpu_busspeed / 4.0; break; case 0xc0: bus_clk = cpu_busspeed / 3.0; break; case 0xe0: bus_clk = cpu_busspeed / 2.0; break; } cpu_set_isa_speed((int) round(bus_clk)); } 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; } dev->smram_enabled = (ram_base != 0x00000000); if (ram_base != 0x00000000) smram_enable(dev->smram, host_base, ram_base, 0x00010000, (val & 0x10), 1); sis_85c4xx_recalcremap(dev); } 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; default: break; } } else if ((dev->reg_base == 0x60) && (dev->cur_reg == 0x00)) dev->reg_00 = val; dev->cur_reg = 0x00; break; case 0xe1: case 0xe2: dev->scratch[port - 0xe1] = val; return; default: break; } } 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); /* On the SiS 40x, the shadow RAM read and write enable bits are write-only! */ if ((dev->reg_base == 0x60) && (dev->cur_reg == 0x62)) ret = dev->regs[rel_reg] & 0x3f; else if ((dev->cur_reg >= dev->reg_base) && (dev->cur_reg <= dev->reg_last)) ret = dev->regs[rel_reg]; else if ((dev->reg_base == 0x60) && (dev->cur_reg == 0x00)) ret = dev->reg_00; if (dev->reg_base != 0x60) dev->cur_reg = 0x00; break; case 0xe1: case 0xe2: ret = dev->scratch[port - 0xe1]; break; default: break; } return ret; } static void sis_85c4xx_reset(void *priv) { sis_85c4xx_t *dev = (sis_85c4xx_t *) priv; int mem_size_mb = mem_size >> 10; memset(dev->regs, 0x00, sizeof(dev->regs)); if (cpu_s->rspeed < 25000000) dev->regs[0x08] = 0x80; if (dev->is_471) { dev->regs[0x09] = 0x40; if (!strcmp(machine_get_internal_name(), "vli486sv2g")) { if (mem_size_mb == 64) dev->regs[0x09] |= 0x1f; else dev->regs[0x09] |= ram_asus[mem_size_mb]; } else if (mem_size_mb >= 64) { if ((mem_size_mb >= 64) && (mem_size_mb < 68)) dev->regs[0x09] |= 0x33; else if ((mem_size_mb >= 68) && (mem_size_mb < 72)) dev->regs[0x09] |= 0x2b; else if ((mem_size_mb >= 72) && (mem_size_mb < 80)) dev->regs[0x09] |= 0x2d; else if ((mem_size_mb >= 80) && (mem_size_mb < 96)) dev->regs[0x09] |= 0x2f; else if ((mem_size_mb >= 96) && (mem_size_mb < 128)) dev->regs[0x09] |= 0x34; else dev->regs[0x09] |= 0x35; } else if (!strcmp(machine_get_internal_name(), "tg486g")) dev->regs[0x09] |= ram_tg486g[mem_size_mb]; else dev->regs[0x09] |= ram_471[mem_size_mb]; dev->ram_banks_val = dev->regs[0x09] & 0x3f; dev->regs[0x09] = 0x00; uint32_t *banks = banks_471[dev->ram_banks_val]; uint32_t b_ex[8] = { 0x00000000 }; uint32_t size = 0x00000000; sis_85c471_banks_split(b_ex, banks); for (uint8_t i = 0; i < 8; i++) { dev->ram_banks[i].phys_base = size; dev->ram_banks[i].phys_size = b_ex[i]; size += b_ex[i]; } dev->regs[0x11] = 0x09; dev->regs[0x12] = 0xff; dev->regs[0x1f] = 0x20; /* Video access enabled. */ dev->regs[0x23] = 0xf0; dev->regs[0x26] = 0x01; smram_enable(dev->smram, 0x000e0000, 0x000a0000, 0x00010000, 0, 1); port_92_remove(dev->port_92); soft_reset_mask = 0; sis_85c471_banks_recalc(dev); kbc_at_set_fast_reset(1); cpu_cpurst_on_sr = 0; } else { /* Bits 6 and 7 must be clear on the SiS 40x. */ if (dev->reg_base == 0x60) dev->reg_00 = 0x24; if (mem_size_mb == 64) dev->regs[0x00] = 0x1f; else if (mem_size_mb < 64) dev->regs[0x00] = ram_4xx[mem_size_mb]; dev->regs[0x11] = 0x01; } dev->scratch[0] = dev->scratch[1] = 0xff; cpu_cache_ext_enabled = 0; cpu_update_waitstates(); dev->force_flush = 1; sis_85c4xx_recalcmapping(dev); if (dev->reg_base == 0x50) cpu_set_isa_speed((int) round(7159091.0)); } 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) { sis_85c4xx_t *dev = (sis_85c4xx_t *) calloc(1, sizeof(sis_85c4xx_t)); dev->is_471 = (info->local >> 8) & 0xff; dev->reg_base = info->local & 0xff; if (dev->is_471) { dev->reg_last = 0x76; dev->smram = smram_add(); dev->port_92 = device_add(&port_92_device); for (uint8_t i = 0; i < 8; i++) { mem_mapping_add(&dev->ram_banks[i].mapping, 0x00000000, 0x00000000, sis_85c471_read_ram, sis_85c471_read_ramw, sis_85c471_read_raml, sis_85c471_write_ram, sis_85c471_write_ramw, sis_85c471_write_raml, NULL, MEM_MAPPING_INTERNAL, &(dev->ram_banks[i])); mem_mapping_disable(&dev->ram_banks[i].mapping); } } else dev->reg_last = dev->reg_base + 0x11; io_sethandler(0x0022, 0x0002, sis_85c4xx_in, NULL, NULL, sis_85c4xx_out, NULL, NULL, dev); io_sethandler(0x00e1, 0x0002, sis_85c4xx_in, NULL, NULL, sis_85c4xx_out, NULL, NULL, dev); sis_85c4xx_reset(dev); return dev; } const device_t sis_85c401_device = { .name = "SiS 85c401/85c402", .internal_name = "sis_85c401", .flags = 0, .local = 0x060, .init = sis_85c4xx_init, .close = sis_85c4xx_close, .reset = sis_85c4xx_reset, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t sis_85c460_device = { .name = "SiS 85c460", .internal_name = "sis_85c460", .flags = 0, .local = 0x050, .init = sis_85c4xx_init, .close = sis_85c4xx_close, .reset = sis_85c4xx_reset, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; /* TODO: Log to make sure the registers are correct. */ const device_t sis_85c461_device = { .name = "SiS 85c461", .internal_name = "sis_85c461", .flags = 0, .local = 0x050, .init = sis_85c4xx_init, .close = sis_85c4xx_close, .reset = sis_85c4xx_reset, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, .config = NULL }; const device_t sis_85c471_device = { .name = "SiS 85c407/85c471", .internal_name = "sis_85c471", .flags = 0, .local = 0x150, .init = sis_85c4xx_init, .close = sis_85c4xx_close, .reset = sis_85c4xx_reset, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, .config = NULL };