/* * 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 Winbond W83877F Super I/O Chip. * * Winbond W83877F Super I/O Chip * Used by the Award 430HX * * Version: @(#)sio_w83877f.c 1.0.11 2018/04/29 * * Author: Miran Grca, * Copyright 2016-2018 Miran Grca. */ #include #include #include #include #include "86box.h" #include "machine/machine.h" #include "device.h" #include "io.h" #include "pci.h" #include "mem.h" #include "rom.h" #include "lpt.h" #include "serial.h" #include "floppy/fdd.h" #include "floppy/fdc.h" #include "sio.h" static int w83877f_locked; static int w83877f_rw_locked = 0; static int w83877f_curreg = 0; static uint8_t w83877f_regs[0x2A]; static uint8_t tries; static fdc_t *w83877f_fdc; static int winbond_port = 0x3f0; static int winbond_key = 0x89; static int winbond_key_times = 1; void w83877f_write(uint16_t port, uint8_t val, void *priv); uint8_t w83877f_read(uint16_t port, void *priv); #define OCSS0 (w83877f_regs[0] & 1) #define OCSS1 ((w83877f_regs[0] >> 1) & 1) #define PRTMODS0 ((w83877f_regs[0] >> 2) & 1) #define PRTMODS1 ((w83877f_regs[0] >> 3) & 1) #define ABCHG (w83877f_regs[1] >> 7) #define CEA (w83877f_regs[2] & 1) #define EA3 ((w83877f_regs[2] >> 1) & 1) #define EA4 ((w83877f_regs[2] >> 2) & 1) #define EA5 ((w83877f_regs[2] >> 3) & 1) #define EA6 ((w83877f_regs[2] >> 4) & 1) #define EA7 ((w83877f_regs[2] >> 5) & 1) #define EA8 ((w83877f_regs[2] >> 6) & 1) #define EA9 (w83877f_regs[2] >> 7) #define SUBMIDI (w83877f_regs[3] & 1) #define SUAMIDI ((w83877f_regs[3] >> 1) & 1) #define GMODS ((w83877f_regs[3] >> 4) & 1) #define EPPVER ((w83877f_regs[3] >> 5) & 1) #define GMENL ((w83877f_regs[3] >> 6) & 1) #define URBTRI (w83877f_regs[4] & 1) #define URATRI ((w83877f_regs[4] >> 1) & 1) #define GMTRI ((w83877f_regs[4] >> 2) & 1) #define PRTTRI ((w83877f_regs[4] >> 3) & 1) #define URBPWD ((w83877f_regs[4] >> 4) & 1) #define URAPWD ((w83877f_regs[4] >> 5) & 1) #define GMPWD ((w83877f_regs[4] >> 6) & 1) #define PRTPWD (w83877f_regs[4] >> 7) #define ECPFTHR0 (w83877f_regs[5] & 1) #define ECPFTHR1 ((w83877f_regs[5] >> 1) & 1) #define ECPFTHR2 ((w83877f_regs[5] >> 2) & 1) #define ECPFTHR3 ((w83877f_regs[5] >> 3) & 1) #define IDETRI (w83877f_regs[6] & 1) #define FDCTRI ((w83877f_regs[6] >> 1) & 1) #define IDEPWD ((w83877f_regs[6] >> 2) & 1) #define FDCPWD ((w83877f_regs[6] >> 3) & 1) #define FIPURDWM ((w83877f_regs[6] >> 4) & 1) #define SEL4FDD ((w83877f_regs[6] >> 5) & 1) #define OSCS2 ((w83877f_regs[6] >> 6) & 1) #define FDDA_TYPE (w83877f_regs[7] & 3) #define FDDB_TYPE ((w83877f_regs[7] >> 2) & 3) #define FDDC_TYPE ((w83877f_regs[7] >> 4) & 3) #define FDDD_TYPE ((w83877f_regs[7] >> 6) & 3) #define FD_BOOT (w83877f_regs[8] & 3) #define MEDIA_ID ((w83877f_regs[8] >> 2) & 3) #define SWWP ((w83877f_regs[8] >> 4) & 1) #define DISFDDWR ((w83877f_regs[8] >> 5) & 1) #define APDTMS2 ((w83877f_regs[8] >> 6) & 1) #define APDTMS1 (w83877f_regs[8] >> 7) #define CHIP_ID (w83877f_regs[9] & 0xF) #define EN3MODE ((w83877f_regs[9] >> 5) & 1) #define LOCKREG ((w83877f_regs[9] >> 6) & 1) #define PRTMODS2 ((w83877f_regs[9] >> 7) & 1) #define PEXTECPP (w83877f_regs[0xA] & 1) #define PEXT_ECP ((w83877f_regs[0xA] >> 1) & 1) #define PEXT_EPP ((w83877f_regs[0xA] >> 2) & 1) #define PEXT_ADP ((w83877f_regs[0xA] >> 3) & 1) #define PDCACT ((w83877f_regs[0xA] >> 4) & 1) #define PDIRHOP ((w83877f_regs[0xA] >> 5) & 1) #define PEXT_ACT ((w83877f_regs[0xA] >> 6) & 1) #define PFDCACT (w83877f_regs[0xA] >> 7) #define DRV2EN_NEG (w83877f_regs[0xB] & 1) /* 0 = drive 2 installed */ #define INVERTZ ((w83877f_regs[0xB] >> 1) & 1) /* 0 = invert DENSEL polarity */ #define MFM ((w83877f_regs[0xB] >> 2) & 1) #define IDENT ((w83877f_regs[0xB] >> 3) & 1) #define ENIFCHG ((w83877f_regs[0xB] >> 4) & 1) #define TX2INV (w83877f_regs[0xC] & 1) #define RX2INV ((w83877f_regs[0xC] >> 1) & 1) #define URIRSEL ((w83877f_regs[0xC] >> 3) & 1) #define HEFERE ((w83877f_regs[0xC] >> 5) & 1) #define TURB ((w83877f_regs[0xC] >> 6) & 1) #define TURA (w83877f_regs[0xC] >> 7) #define IRMODE0 (w83877f_regs[0xD] & 1) #define IRMODE1 ((w83877f_regs[0xD] >> 1) & 1) #define IRMODE2 ((w83877f_regs[0xD] >> 2) & 1) #define HDUPLX ((w83877f_regs[0xD] >> 3) & 1) #define SIRRX0 ((w83877f_regs[0xD] >> 4) & 1) #define SIRRX1 ((w83877f_regs[0xD] >> 5) & 1) #define SIRTX0 ((w83877f_regs[0xD] >> 6) & 1) #define SIRTX1 (w83877f_regs[0xD] >> 7) #define GIO0AD (w83877f_regs[0x10] | (((uint16_t) w83877f_regs[0x11] & 7) << 8)) #define GIO0CADM (w83877f_regs[0x11] >> 6) #define GIO1AD (w83877f_regs[0x12] | (((uint16_t) w83877f_regs[0x13] & 7) << 8)) #define GIO1CADM (w83877f_regs[0x13] >> 6) #define GDA0IPI (w83877f_regs[0x14] & 1) #define GDA0OPI ((w83877f_regs[0x14] >> 1) & 1) #define GCS0IOW ((w83877f_regs[0x14] >> 2) & 1) #define GCS0IOR ((w83877f_regs[0x14] >> 3) & 1) #define GIO0CSH ((w83877f_regs[0x14] >> 4) & 1) #define GIOP0MD ((w83877f_regs[0x14] >> 5) & 7) #define GDA1IPI (w83877f_regs[0x15] & 1) #define GDA1OPI ((w83877f_regs[0x15] >> 1) & 1) #define GCS1IOW ((w83877f_regs[0x15] >> 2) & 1) #define GCS1IOR ((w83877f_regs[0x15] >> 3) & 1) #define GIO1CSH ((w83877f_regs[0x15] >> 4) & 1) #define GIOP1MD ((w83877f_regs[0x15] >> 5) & 7) #define HEFRAS (w83877f_regs[0x16] & 1) #define IRIDE ((w83877f_regs[0x16] >> 1) & 1) #define PNPCVS ((w83877f_regs[0x16] >> 2) & 1) #define GMDRQ ((w83877f_regs[0x16] >> 3) & 1) #define GOIQSEL ((w83877f_regs[0x16] >> 4) & 1) #define DSUBLGRQ (w83877f_regs[0x17] & 1) #define DSUALGRQ ((w83877f_regs[0x17] >> 1) & 1) #define DSPRLGRQ ((w83877f_regs[0x17] >> 2) & 1) #define DSFDLGRQ ((w83877f_regs[0x17] >> 3) & 1) #define PRIRQOD ((w83877f_regs[0x17] >> 4) & 1) #define GMAS (w83877f_regs[0x1E] & 3) #define GMAD (w83877f_regs[0x1E] & 0xFC) #define FDCAD ((w83877f_regs[0x20] & 0xFC) << 2) /* Main IDE base address. */ #define IDE0AD ((w83877f_regs[0x21] & 0xFC) << 2) /* IDE Alternate status base address. */ #define IDE1AD ((w83877f_regs[0x22] & 0xFC) << 2) #define PRTAD (((uint16_t) w83877f_regs[0x23]) << 2) #define URAAD (((uint16_t) w83877f_regs[0x24] & 0xFE) << 2) #define URBAD (((uint16_t) w83877f_regs[0x25] & 0xFE) << 2) #define PRTDQS (w83877f[regs[0x26] & 0xF) #define FDCDQS (w83877f[regs[0x26] >> 4) #define PRTIQS (w83877f[regs[0x27] & 0xF) #define ECPIRQx (w83877f[regs[0x27] >> 5) #define URBIQS (w83877f[regs[0x28] & 0xF) #define URAIQS (w83877f[regs[0x28] >> 4) #define IQNIQS (w83877f[regs[0x29] & 0xF) #define FDCIQS (w83877f[regs[0x29] >> 4) #define W83757 (!PRTMODS2 && !PRTMODS1 && !PRTMODS0) #define EXTFDC (!PRTMODS2 && !PRTMODS1 && PRTMODS0) #define EXTADP (!PRTMODS2 && PRTMODS1 && !PRTMODS0) #define EXT2FDD (!PRTMODS2 && PRTMODS1 && PRTMODS0) #define JOYSTICK (PRTMODS2 && !PRTMODS1 && !PRTMODS0) #define EPP_SPP (PRTMODS2 && !PRTMODS1 && PRTMODS0) #define ECP (PRTMODS2 && PRTMODS1 && !PRTMODS0) #define ECP_EPP (PRTMODS2 && PRTMODS1 && PRTMODS0) static uint16_t fdc_valid_ports[2] = {0x3F0, 0x370}; static uint16_t ide_valid_ports[2] = {0x1F0, 0x170}; static uint16_t ide_as_valid_ports[2] = {0x3F6, 0x376}; static uint16_t lpt1_valid_ports[3] = {0x3BC, 0x378, 0x278}; static uint16_t com1_valid_ports[9] = {0x3F8, 0x2F8, 0x3E8, 0x2E8}; static uint16_t com2_valid_ports[9] = {0x3F8, 0x2F8, 0x3E8, 0x2E8}; static void w83877f_remap(void) { io_removehandler(0x250, 0x0002, w83877f_read, NULL, NULL, w83877f_write, NULL, NULL, NULL); io_removehandler(0x3f0, 0x0002, w83877f_read, NULL, NULL, w83877f_write, NULL, NULL, NULL); io_sethandler(HEFRAS ? 0x3f0 : 0x250, 0x0002, w83877f_read, NULL, NULL, w83877f_write, NULL, NULL, NULL); winbond_port = (HEFRAS ? 0x3f0 : 0x250); winbond_key_times = HEFRAS + 1; winbond_key = (HEFRAS ? 0x86 : 0x88) | HEFERE; } static uint8_t is_in_array(uint16_t *port_array, uint8_t max, uint16_t port) { uint8_t i = 0; for (i = 0; i < max; i++) { if (port_array[i] == port) return 1; } return 0; } static uint16_t make_port(uint8_t reg) { uint16_t p = 0; switch(reg) { case 0x20: p = ((uint16_t) (w83877f_regs[reg] & 0xfc)) << 2; p &= 0xFF0; if ((p < 0x100) || (p > 0x3F0)) p = 0x3F0; if (!(is_in_array(fdc_valid_ports, 2, p))) p = 0x3F0; w83877f_regs[reg] = ((p >> 2) & 0xfc) | (w83877f_regs[reg] & 3); break; case 0x21: p = ((uint16_t) (w83877f_regs[reg] & 0xfc)) << 2; p &= 0xFF0; if ((p < 0x100) || (p > 0x3F0)) p = 0x1F0; if (!(is_in_array(ide_valid_ports, 2, p))) p = 0x1F0; w83877f_regs[reg] = ((p >> 2) & 0xfc) | (w83877f_regs[reg] & 3); break; case 0x22: p = ((uint16_t) (w83877f_regs[reg] & 0xfc)) << 2; p &= 0xFF0; if ((p < 0x106) || (p > 0x3F6)) p = 0x3F6; if (!(is_in_array(ide_as_valid_ports, 2, p))) p = 0x3F6; w83877f_regs[reg] = ((p >> 2) & 0xfc) | (w83877f_regs[reg] & 3); break; case 0x23: p = ((uint16_t) (w83877f_regs[reg] & 0xff)) << 2; p &= 0xFFC; if ((p < 0x100) || (p > 0x3F8)) p = 0x378; if (!(is_in_array(lpt1_valid_ports, 3, p))) p = 0x378; w83877f_regs[reg] = (p >> 2); break; case 0x24: p = ((uint16_t) (w83877f_regs[reg] & 0xfe)) << 2; p &= 0xFF8; if ((p < 0x100) || (p > 0x3F8)) p = 0x3F8; if (!(is_in_array(com1_valid_ports, 9, p))) p = 0x3F8; w83877f_regs[reg] = ((p >> 2) & 0xfe) | (w83877f_regs[reg] & 1); break; case 0x25: p = ((uint16_t) (w83877f_regs[reg] & 0xfe)) << 2; p &= 0xFF8; if ((p < 0x100) || (p > 0x3F8)) p = 0x2F8; if (!(is_in_array(com2_valid_ports, 9, p))) p = 0x2F8; w83877f_regs[reg] = ((p >> 2) & 0xfe) | (w83877f_regs[reg] & 1); break; } return p; } void w83877f_serial_handler(int id) { int reg_mask = (id - 1) ? 0x10 : 0x20; int reg_id = (id - 1) ? 0x24 : 0x25; int irq_mask = (id - 1) ? 0xF : 0xF0; if ((w83877f_regs[4] & reg_mask) || !(w83877f_regs[reg_id] & 0xc0)) { serial_remove(id); } else { serial_setup(id, make_port(reg_id), w83877f_regs[0x28] & irq_mask); } } void w83877f_write(uint16_t port, uint8_t val, void *priv) { uint8_t index = (port & 1) ? 0 : 1; uint8_t valxor = 0; uint8_t max = 0x2A; if (index) { if ((val == winbond_key) && !w83877f_locked) { if (winbond_key_times == 2) { if (tries) { w83877f_locked = 1; tries = 0; } else { tries++; } } else { w83877f_locked = 1; tries = 0; } } else { if (w83877f_locked) { if (val < max) w83877f_curreg = val; if (val == 0xaa) { w83877f_locked = 0; } } else { if (tries) tries = 0; } } } else { if (w83877f_locked) { if (w83877f_rw_locked) return; if ((w83877f_curreg >= 0x26) && (w83877f_curreg <= 0x27)) return; if (w83877f_curreg == 0x29) return; if (w83877f_curreg == 6) val &= 0xF3; valxor = val ^ w83877f_regs[w83877f_curreg]; w83877f_regs[w83877f_curreg] = val; goto process_value; } } return; process_value: switch(w83877f_curreg) { case 1: if (valxor & 0x80) { fdc_set_swap(w83877f_fdc, (w83877f_regs[1] & 0x80) ? 1 : 0); } break; case 4: if (valxor & 0x10) { w83877f_serial_handler(2); } if (valxor & 0x20) { w83877f_serial_handler(1); } if (valxor & 0x80) { lpt1_remove(); if (!(w83877f_regs[4] & 0x80)) lpt1_init(make_port(0x23)); } break; case 6: if (valxor & 0x08) { fdc_remove(w83877f_fdc); if (!(w83877f_regs[6] & 0x08)) fdc_set_base(w83877f_fdc, 0x03f0); } break; case 7: if (valxor & 3) fdc_update_rwc(w83877f_fdc, 0, FDDA_TYPE); if (valxor & 0xC) fdc_update_rwc(w83877f_fdc, 1, FDDB_TYPE); if (valxor & 0x30) fdc_update_rwc(w83877f_fdc, 2, FDDC_TYPE); if (valxor & 0xC0) fdc_update_rwc(w83877f_fdc, 3, FDDD_TYPE); break; case 8: if (valxor & 3) fdc_update_boot_drive(w83877f_fdc, FD_BOOT); if (valxor & 0x10) fdc_set_swwp(w83877f_fdc, SWWP ? 1 : 0); if (valxor & 0x20) fdc_set_diswr(w83877f_fdc, DISFDDWR ? 1 : 0); break; case 9: if (valxor & 0x20) { fdc_update_enh_mode(w83877f_fdc, EN3MODE ? 1 : 0); } if (valxor & 0x40) { w83877f_rw_locked = (val & 0x40) ? 1 : 0; } break; case 0xB: if (valxor & 1) fdc_update_drv2en(w83877f_fdc, DRV2EN_NEG ? 0 : 1); if (valxor & 2) fdc_update_densel_polarity(w83877f_fdc, INVERTZ ? 1 : 0); break; case 0xC: if (valxor & 0x20) w83877f_remap(); break; case 0x16: if (valxor & 1) w83877f_remap(); break; case 0x23: if (valxor) { lpt1_remove(); if (!(w83877f_regs[4] & 0x80)) lpt1_init(make_port(0x23)); } break; case 0x24: if (valxor & 0xfe) { w83877f_serial_handler(1); } break; case 0x25: if (valxor & 0xfe) { w83877f_serial_handler(2); } break; case 0x28: if (valxor & 0xf) { if ((w83877f_regs[0x28] & 0xf) == 0) w83877f_regs[0x28] |= 0x3; if (!(w83877f_regs[2] & 0x10)) serial_setup(2, make_port(0x25), w83877f_regs[0x28] & 0xF); } if (valxor & 0xf0) { if ((w83877f_regs[0x28] & 0xf0) == 0) w83877f_regs[0x28] |= 0x40; if (!(w83877f_regs[4] & 0x20)) { serial_setup(1, make_port(0x24), (w83877f_regs[0x28] & 0xF0) >> 8); } } break; } } uint8_t w83877f_read(uint16_t port, void *priv) { uint8_t index = (port & 1) ? 0 : 1; if (!w83877f_locked) { return 0xff; } if (index) { return w83877f_curreg; } else { if ((w83877f_curreg < 0x18) && w83877f_rw_locked) return 0xff; if (w83877f_curreg == 7) return (fdc_get_rwc(w83877f_fdc, 0) | (fdc_get_rwc(w83877f_fdc, 1) << 2)); return w83877f_regs[w83877f_curreg]; } } void w83877f_reset(void) { lpt1_remove(); lpt1_init(0x378); fdc_reset(w83877f_fdc); memset(w83877f_regs, 0, 0x2A); w83877f_regs[3] = 0x30; w83877f_regs[7] = 0xF5; w83877f_regs[9] = 0x0A; w83877f_regs[0xA] = 0x1F; w83877f_regs[0xC] = 0x28; w83877f_regs[0xD] = 0xA3; w83877f_regs[0x16] = (romset == ROM_PRESIDENT) ? 4 : 5; w83877f_regs[0x1E] = 0x81; w83877f_regs[0x20] = (0x3f0 >> 2) & 0xfc; w83877f_regs[0x21] = (0x1f0 >> 2) & 0xfc; w83877f_regs[0x22] = ((0x3f6 >> 2) & 0xfc) | 1; w83877f_regs[0x23] = (0x378 >> 2); w83877f_regs[0x24] = (0x3f8 >> 2) & 0xfe; w83877f_regs[0x25] = (0x2f8 >> 2) & 0xfe; w83877f_regs[0x26] = (2 << 4) | 4; w83877f_regs[0x27] = (6 << 4) | 7; w83877f_regs[0x28] = (4 << 4) | 3; w83877f_regs[0x29] = 0x62; serial_setup(1, SERIAL1_ADDR, SERIAL1_IRQ); serial_setup(2, SERIAL2_ADDR, SERIAL2_IRQ); w83877f_remap(); w83877f_locked = 0; w83877f_rw_locked = 0; } void w83877f_init(void) { w83877f_fdc = device_add(&fdc_at_winbond_device); lpt2_remove(); w83877f_reset(); }