/* * 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 Realtek RTG series of VGA ISA chips. * * * * Authors: TheCollector1995, * * Copyright 2021 TheCollector1995. */ #include #include #include #include #include #include <86box/86box.h> #include <86box/io.h> #include <86box/mem.h> #include <86box/rom.h> #include <86box/device.h> #include <86box/timer.h> #include <86box/video.h> #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> #define BIOS_ROM_PATH "roms/video/rtg/realtekrtg3106.BIN" typedef struct { const char *name; int type; svga_t svga; rom_t bios_rom; uint8_t bank3d6, bank3d7; uint32_t vram_size, vram_mask; } rtg_t; static video_timings_t timing_rtg_isa = {VIDEO_ISA, 3, 3, 6, 5, 5, 10}; static void rtg_recalcbanking(rtg_t *dev) { svga_t *svga = &dev->svga; svga->write_bank = (dev->bank3d7 & 0x0f) * 65536; if (svga->gdcreg[0x0f] & 4) svga->read_bank = (dev->bank3d6 & 0x0f) * 65536; else svga->read_bank = svga->write_bank; } static uint8_t rtg_in(uint16_t addr, void *priv) { rtg_t *dev = (rtg_t *)priv; svga_t *svga = &dev->svga; uint8_t ret = 0; if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1)) addr ^= 0x60; switch (addr) { case 0x3ce: return svga->gdcaddr; case 0x3cf: if (svga->gdcaddr == 0x0c) return svga->gdcreg[0x0c] | 4; else if ((svga->gdcaddr > 8) && (svga->gdcaddr != 0x0c)) return svga->gdcreg[svga->gdcaddr]; break; case 0x3d4: return svga->crtcreg; case 0x3d5: if (!(svga->crtc[0x1e] & 0x80) && (svga->crtcreg > 0x18)) return 0xff; if (svga->crtcreg == 0x1a) return dev->type << 6; if (svga->crtcreg == 0x1e) { if (dev->vram_size == 1024) ret = 2; else if (dev->vram_size == 512) ret = 1; else ret = 0; return svga->crtc[0x1e] | ret; } return svga->crtc[svga->crtcreg]; case 0x3d6: return dev->bank3d6; case 0x3d7: return dev->bank3d7; } return svga_in(addr, svga); } static void rtg_out(uint16_t addr, uint8_t val, void *priv) { rtg_t *dev = (rtg_t *)priv; svga_t *svga = &dev->svga; uint8_t old; if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1)) addr ^= 0x60; switch (addr) { case 0x3ce: svga->gdcaddr = val; return; case 0x3cf: if (svga->gdcaddr > 8) { svga->gdcreg[svga->gdcaddr] = val; switch (svga->gdcaddr) { case 0x0b: case 0x0c: svga->fullchange = changeframecount; svga_recalctimings(svga); break; case 0x0f: rtg_recalcbanking(dev); return; } } break; case 0x3d4: svga->crtcreg = val & 0x3f; return; case 0x3d5: if ((svga->crtcreg < 7) && (svga->crtc[0x11] & 0x80)) return; if ((svga->crtcreg == 7) && (svga->crtc[0x11] & 0x80)) val = (svga->crtc[7] & ~0x10) | (val & 0x10); old = svga->crtc[svga->crtcreg]; svga->crtc[svga->crtcreg] = val; if (svga->crtc[0x1e] & 0x80) { switch (svga->crtcreg) { case 0x19: svga->vram_display_mask = (val & 0x20) ? dev->vram_mask : 0x3ffff; svga->fullchange = changeframecount; svga_recalctimings(svga); break; } } if (old != val) { if (svga->crtcreg < 0xe || svga->crtcreg > 0x10) { if ((svga->crtcreg == 0xc) || (svga->crtcreg == 0xd)) { svga->fullchange = 3; svga->ma_latch = ((svga->crtc[0xc] << 8) | svga->crtc[0xd]) + ((svga->crtc[8] & 0x60) >> 5); } else { svga->fullchange = changeframecount; svga_recalctimings(svga); } } } break; case 0x3d6: dev->bank3d6 = val; rtg_recalcbanking(dev); return; case 0x3d7: dev->bank3d7 = val; rtg_recalcbanking(dev); return; } svga_out(addr, val, svga); } static void rtg_recalctimings(svga_t *svga) { svga->ma_latch = ((svga->crtc[0xc] << 8) | svga->crtc[0xd]) + ((svga->crtc[8] & 0x60) >> 5); svga->ma_latch |= ((svga->crtc[0x19] & 0x10) << 16) | ((svga->crtc[0x19] & 0x40) << 17); svga->interlace = (svga->crtc[0x19] & 1); svga->lowres = svga->attrregs[0x10] & 0x40; /*Clock table not available, currently a guesswork*/ switch (((svga->miscout >> 2) & 3) | ((svga->gdcreg[0x0c] & 0x20) >> 3)) { case 0: case 1: break; case 2: svga->clock = (cpuclock * (double)(1ull << 32)) / 36000000.0; break; case 3: svga->clock = (cpuclock * (double)(1ull << 32)) / 65100000.0; break; case 4: svga->clock = (cpuclock * (double)(1ull << 32)) / 44900000.0; break; case 5: svga->clock = (cpuclock * (double)(1ull << 32)) / 50000000.0; break; case 6: svga->clock = (cpuclock * (double)(1ull << 32)) / 80000000.0; break; case 7: svga->clock = (cpuclock * (double)(1ull << 32)) / 75000000.0; break; } switch (svga->gdcreg[0x0c] & 3) { case 1: svga->clock /= 1.5; break; case 2: svga->clock /= 2; break; case 3: svga->clock /= 4; break; } if ((svga->gdcreg[6] & 1) || (svga->attrregs[0x10] & 1)) { switch (svga->gdcreg[5] & 0x60) { case 0x00: if (svga->seqregs[1] & 8) /*Low res (320)*/ svga->render = svga_render_4bpp_lowres; else { svga->hdisp = svga->crtc[1] - ((svga->crtc[5] & 0x60) >> 5); svga->hdisp++; svga->hdisp *= 8; if (svga->hdisp == 1280) svga->rowoffset >>= 1; svga->render = svga_render_4bpp_highres; } break; case 0x20: /*4 colours*/ if (svga->seqregs[1] & 8) /*Low res (320)*/ svga->render = svga_render_2bpp_lowres; else svga->render = svga_render_2bpp_highres; break; case 0x40: case 0x60: svga->hdisp = svga->crtc[1] - ((svga->crtc[5] & 0x60) >> 5); svga->hdisp++; svga->hdisp *= (svga->seqregs[1] & 8) ? 16 : 8; if (svga->crtc[0x19] & 2) { if (svga->hdisp == 1280) { svga->hdisp >>= 1; } else svga->rowoffset <<= 1; svga->render = svga_render_8bpp_highres; } else { if (svga->lowres) svga->render = svga_render_8bpp_lowres; else svga->render = svga_render_8bpp_highres; } break; } } } static void * rtg_init(const device_t *info) { const char *fn; rtg_t *dev; dev = (rtg_t *)malloc(sizeof(rtg_t)); memset(dev, 0x00, sizeof(rtg_t)); dev->name = info->name; dev->type = info->local; fn = BIOS_ROM_PATH; switch(dev->type) { case 2: /* ISA RTG3106 */ dev->vram_size = device_get_config_int("memory") << 10; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_rtg_isa); svga_init(info, &dev->svga, dev, dev->vram_size, rtg_recalctimings, rtg_in, rtg_out, NULL, NULL); io_sethandler(0x03c0, 32, rtg_in,NULL,NULL, rtg_out,NULL,NULL, dev); break; } dev->svga.bpp = 8; dev->svga.miscout = 1; dev->vram_mask = dev->vram_size - 1; rom_init(&dev->bios_rom, (char *) fn, 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); return(dev); } static void rtg_close(void *priv) { rtg_t *dev = (rtg_t *)priv; svga_close(&dev->svga); free(dev); } static void rtg_speed_changed(void *priv) { rtg_t *dev = (rtg_t *)priv; svga_recalctimings(&dev->svga); } static void rtg_force_redraw(void *priv) { rtg_t *dev = (rtg_t *)priv; dev->svga.fullchange = changeframecount; } static int rtg_available(void) { return rom_present(BIOS_ROM_PATH); } static const device_config_t rtg_config[] = { { "memory", "Memory size", CONFIG_SELECTION, "", 1024, "", { 0 }, { { "256 KB", 256 }, { "512 KB", 512 }, { "1 MB", 1024 }, { "" } } }, { "", "", -1 } }; const device_t realtek_rtg3106_device = { "Realtek RTG3106 (ISA)", DEVICE_ISA | DEVICE_AT, 2, rtg_init, rtg_close, NULL, { rtg_available }, rtg_speed_changed, rtg_force_redraw, rtg_config };