/* * 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 ALi M6117 SoC. * * * * Authors: RichardG, * * Copyright 2020 RichardG. */ #include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/mem.h> #include <86box/io.h> #include <86box/pci.h> #include <86box/pic.h> #include <86box/timer.h> #include <86box/pit.h> #include <86box/device.h> #include <86box/port_92.h> #include <86box/usb.h> #include <86box/hdc.h> #include <86box/hdc_ide.h> #include <86box/chipset.h> typedef struct ali6117_t { uint32_t local; /* Main registers (port 22h/23h) */ uint8_t unlocked; uint8_t reg_offset; uint8_t regs[256]; } ali6117_t; #ifdef ENABLE_ALI6117_LOG int ali6117_do_log = ENABLE_ALI6117_LOG; static void ali6117_log(const char *fmt, ...) { va_list ap; if (ali6117_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else #define ali6117_log(fmt, ...) #endif static void ali6117_recalcmapping(ali6117_t *dev) { uint8_t reg, bitpair; uint32_t base, size; int state; shadowbios = 0; shadowbios_write = 0; ali6117_log("ALI6117: Shadowing for A0000-BFFFF (reg 12 bit 1) = %s\n", (dev->regs[0x12] & 0x02) ? "on" : "off"); mem_set_mem_state(0xa0000, 0x20000, (dev->regs[0x12] & 0x02) ? (MEM_WRITE_INTERNAL | MEM_READ_INTERNAL) : (MEM_WRITE_EXTANY | MEM_READ_EXTANY)); for (reg = 0; reg <= 1; reg++) { for (bitpair = 0; bitpair <= 3; bitpair++) { size = 0x8000; base = 0xc0000 + (size * ((reg * 4) + bitpair)); ali6117_log("ALI6117: Shadowing for %05X-%05X (reg %02X bp %d wmask %02X rmask %02X) =", base, base + size - 1, 0x14 + reg, bitpair, 1 << ((bitpair * 2) + 1), 1 << (bitpair * 2)); state = 0; if (dev->regs[0x14 + reg] & (1 << ((bitpair * 2) + 1))) { ali6117_log(" w on"); state |= MEM_WRITE_INTERNAL; if (base >= 0xe0000) shadowbios_write |= 1; } else { ali6117_log(" w off"); state |= MEM_WRITE_EXTANY; } if (dev->regs[0x14 + reg] & (1 << (bitpair * 2))) { ali6117_log("; r on\n"); state |= MEM_READ_INTERNAL; if (base >= 0xe0000) shadowbios |= 1; } else { ali6117_log("; r off\n"); state |= MEM_READ_EXTANY; } mem_set_mem_state(base, size, state); } } flushmmucache(); } static void ali6117_reg_write(uint16_t addr, uint8_t val, void *priv) { ali6117_t *dev = (ali6117_t *) priv; ali6117_log("ALI6117: reg_write(%04X, %02X)\n", addr, val); if (addr == 0x22) dev->reg_offset = val; else if (dev->reg_offset == 0x13) dev->unlocked = (val == 0xc5); else if (dev->unlocked) { ali6117_log("ALI6117: regs[%02X] = %02X\n", dev->reg_offset, val); switch (dev->reg_offset) { case 0x30: case 0x34: case 0x35: case 0x3e: case 0x3f: case 0x46: case 0x4c: case 0x6a: case 0x73: return; /* read-only registers */ case 0x12: val &= 0xf7; /* FALL-THROUGH */ case 0x14: case 0x15: dev->regs[dev->reg_offset] = val; ali6117_recalcmapping(dev); break; case 0x1e: val &= 0x07; switch (val) { /* Half PIT clock. */ case 0x0: cpu_set_isa_speed(7159091); break; /* Divisors on the input clock PCLK2, which is double the CPU clock. */ case 0x1: cpu_set_isa_speed(cpu_busspeed / 1.5); break; case 0x2: cpu_set_isa_speed(cpu_busspeed / 2); break; case 0x3: cpu_set_isa_speed(cpu_busspeed / 2.5); break; case 0x4: cpu_set_isa_speed(cpu_busspeed / 3); break; case 0x5: cpu_set_isa_speed(cpu_busspeed / 4); break; case 0x6: cpu_set_isa_speed(cpu_busspeed / 5); break; case 0x7: cpu_set_isa_speed(cpu_busspeed / 6); break; } break; case 0x20: val &= 0xbf; refresh_at_enable = !!(val & 0x80); break; case 0x31: /* TODO: fast gate A20 (bit 0) */ val &= 0x21; break; case 0x32: val &= 0xc1; break; case 0x33: val &= 0xfd; break; case 0x36: val &= 0xf0; val |= dev->regs[dev->reg_offset]; break; case 0x37: val &= 0xf5; break; case 0x3c: val &= 0x8f; ide_pri_disable(); ide_set_base(1, (val & 0x01) ? 0x170 : 0x1f0); ide_set_side(1, (val & 0x01) ? 0x376 : 0x3f6); ide_pri_enable(); break; case 0x44: case 0x45: val &= 0x3f; break; case 0x4a: val &= 0xfe; break; case 0x55: val &= 0x03; break; case 0x56: val &= 0xc7; break; case 0x58: val &= 0xc3; break; case 0x59: val &= 0x60; break; case 0x5b: val &= 0x1f; break; case 0x64: val &= 0xf7; break; case 0x66: val &= 0xe3; break; case 0x67: val &= 0xdf; break; case 0x69: val &= 0x50; break; case 0x6b: val &= 0x7f; break; case 0x6e: case 0x6f: val &= 0x03; break; case 0x71: val &= 0x1f; break; } dev->regs[dev->reg_offset] = val; } } static uint8_t ali6117_reg_read(uint16_t addr, void *priv) { ali6117_t *dev = (ali6117_t *) priv; uint8_t ret; if (addr == 0x22) ret = dev->reg_offset; else ret = dev->regs[dev->reg_offset]; ali6117_log("ALI6117: reg_read(%04X) = %02X\n", dev->reg_offset, ret); return ret; } static void ali6117_reset(void *priv) { ali6117_t *dev = (ali6117_t *) priv; ali6117_log("ALI6117: reset()\n"); memset(dev->regs, 0, sizeof(dev->regs)); dev->regs[0x11] = 0xf8; dev->regs[0x12] = 0x20; dev->regs[0x17] = 0xff; dev->regs[0x18] = 0xf0; dev->regs[0x1a] = 0xff; dev->regs[0x1b] = 0xf0; dev->regs[0x1d] = 0xff; dev->regs[0x20] = 0x80; dev->regs[0x30] = 0x08; dev->regs[0x31] = 0x01; dev->regs[0x34] = 0x04; /* enable internal RTC */ dev->regs[0x35] = 0x20; /* enable internal KBC */ dev->regs[0x36] = dev->local & 0x4; /* M6117D ID */ cpu_set_isa_speed(7159091); } static void ali6117_setup(ali6117_t *dev) { ali6117_log("ALI6117: setup()\n"); /* Main register interface */ io_sethandler(0x22, 2, ali6117_reg_read, NULL, NULL, ali6117_reg_write, NULL, NULL, dev); } static void ali6117_close(void *priv) { ali6117_t *dev = (ali6117_t *) priv; ali6117_log("ALI6117: close()\n"); io_removehandler(0x22, 2, ali6117_reg_read, NULL, NULL, ali6117_reg_write, NULL, NULL, dev); free(dev); } static void * ali6117_init(const device_t *info) { ali6117_log("ALI6117: init()\n"); ali6117_t *dev = (ali6117_t *) malloc(sizeof(ali6117_t)); memset(dev, 0, sizeof(ali6117_t)); dev->local = info->local; device_add(&ide_isa_device); ali6117_setup(dev); ali6117_reset(dev); pic_elcr_io_handler(0); refresh_at_enable = 0; return dev; } const device_t ali6117d_device = { "ALi M6117D", DEVICE_AT, 0x2, ali6117_init, ali6117_close, ali6117_reset, { NULL }, NULL, NULL, NULL };