/* * 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 WD76C10 System Controller chip. * * * * Authors: Sarah Walker, * Miran Grca, * Fred N. van Kempen, * * Copyright 2008-2019 Sarah Walker. * Copyright 2016-2019 Miran Grca. * Copyright 2017-2019 Fred N. van Kempen. */ #include #include #include #include #include #include <86box/86box.h> #include <86box/device.h> #include <86box/timer.h> #include <86box/io.h> #include <86box/keyboard.h> #include <86box/mem.h> #include <86box/port_92.h> #include <86box/serial.h> #include <86box/fdd.h> #include <86box/fdc.h> #include <86box/video.h> #include <86box/chipset.h> typedef struct { int type; uint16_t reg_0092; uint16_t reg_2072; uint16_t reg_2872; uint16_t reg_5872; uint16_t reg_f872; serial_t *uart[2]; fdc_t *fdc; mem_mapping_t extram_mapping; uint8_t extram[65536]; } wd76c10_t; static uint16_t wd76c10_read(uint16_t port, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; int16_t ret = 0xffff; switch (port) { case 0x2072: ret = dev->reg_2072; break; case 0x2872: ret = dev->reg_2872; break; case 0x5872: ret = dev->reg_5872; break; case 0xf872: ret = dev->reg_f872; break; } return(ret); } static void wd76c10_write(uint16_t port, uint16_t val, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; switch (port) { case 0x2072: dev->reg_2072 = val; serial_remove(dev->uart[0]); if (!(val & 0x10)) { switch ((val >> 5) & 7) { case 1: serial_setup(dev->uart[0], 0x3f8, 4); break; case 2: serial_setup(dev->uart[0], 0x2f8, 4); break; case 3: serial_setup(dev->uart[0], 0x3e8, 4); break; case 4: serial_setup(dev->uart[0], 0x2e8, 4); break; default: break; } } serial_remove(dev->uart[1]); if (!(val & 0x01)) { switch ((val >> 1) & 7) { case 1: serial_setup(dev->uart[1], 0x3f8, 3); break; case 2: serial_setup(dev->uart[1], 0x2f8, 3); break; case 3: serial_setup(dev->uart[1], 0x3e8, 3); break; case 4: serial_setup(dev->uart[1], 0x2e8, 3); break; default: break; } } break; case 0x2872: dev->reg_2872 = val; fdc_remove(dev->fdc); if (! (val & 1)) fdc_set_base(dev->fdc, 0x03f0); break; case 0x5872: dev->reg_5872 = val; break; case 0xf872: dev->reg_f872 = val; switch (val & 3) { case 0: mem_set_mem_state(0xd0000, 0x10000, MEM_READ_EXTANY | MEM_WRITE_EXTANY); break; case 1: mem_set_mem_state(0xd0000, 0x10000, MEM_READ_INTERNAL | MEM_WRITE_EXTANY); break; case 2: mem_set_mem_state(0xd0000, 0x10000, MEM_READ_EXTANY | MEM_WRITE_INTERNAL); break; case 3: mem_set_mem_state(0xd0000, 0x10000, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL); break; } flushmmucache_nopc(); if (val & 4) mem_mapping_enable(&dev->extram_mapping); else mem_mapping_disable(&dev->extram_mapping); flushmmucache_nopc(); break; } } static uint8_t wd76c10_readb(uint16_t port, void *priv) { if (port & 1) return(wd76c10_read(port & ~1, priv) >> 8); return(wd76c10_read(port, priv) & 0xff); } static void wd76c10_writeb(uint16_t port, uint8_t val, void *priv) { uint16_t temp = wd76c10_read(port, priv); if (port & 1) wd76c10_write(port & ~1, (temp & 0x00ff) | (val << 8), priv); else wd76c10_write(port , (temp & 0xff00) | val, priv); } uint8_t wd76c10_read_extram(uint32_t addr, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; return dev->extram[addr & 0xffff]; } uint16_t wd76c10_read_extramw(uint32_t addr, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; return *(uint16_t *)&dev->extram[addr & 0xffff]; } uint32_t wd76c10_read_extraml(uint32_t addr, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; return *(uint32_t *)&dev->extram[addr & 0xffff]; } void wd76c10_write_extram(uint32_t addr, uint8_t val, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; dev->extram[addr & 0xffff] = val; } void wd76c10_write_extramw(uint32_t addr, uint16_t val, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; *(uint16_t *)&dev->extram[addr & 0xffff] = val; } void wd76c10_write_extraml(uint32_t addr, uint32_t val, void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; *(uint32_t *)&dev->extram[addr & 0xffff] = val; } static void wd76c10_close(void *priv) { wd76c10_t *dev = (wd76c10_t *)priv; free(dev); } static void * wd76c10_init(const device_t *info) { wd76c10_t *dev; dev = (wd76c10_t *) malloc(sizeof(wd76c10_t)); memset(dev, 0x00, sizeof(wd76c10_t)); dev->type = info->local; dev->fdc = (fdc_t *)device_add(&fdc_at_device); dev->uart[0] = device_add_inst(&i8250_device, 1); dev->uart[1] = device_add_inst(&i8250_device, 2); device_add(&port_92_word_device); io_sethandler(0x2072, 2, wd76c10_readb,wd76c10_read,NULL, wd76c10_writeb,wd76c10_write,NULL, dev); io_sethandler(0x2872, 2, wd76c10_readb,wd76c10_read,NULL, wd76c10_writeb,wd76c10_write,NULL, dev); io_sethandler(0x5872, 2, wd76c10_readb,wd76c10_read,NULL, wd76c10_writeb,wd76c10_write,NULL, dev); io_sethandler(0xf872, 2, wd76c10_readb,wd76c10_read,NULL, wd76c10_writeb,wd76c10_write,NULL, dev); mem_mapping_add(&dev->extram_mapping, 0xd0000, 0x10000, wd76c10_read_extram,wd76c10_read_extramw,wd76c10_read_extraml, wd76c10_write_extram,wd76c10_write_extramw,wd76c10_write_extraml, dev->extram, MEM_MAPPING_EXTERNAL, dev); mem_mapping_disable(&dev->extram_mapping); return(dev); } const device_t wd76c10_device = { "WD 76C10", 0, 0, wd76c10_init, wd76c10_close, NULL, { NULL }, NULL, NULL, NULL };