/* * 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. * * Generic SVGA handling. * * This is intended to be used by another SVGA driver, * and not as a card in its own right. * * * * Authors: Sarah Walker, * Miran Grca, * * Copyright 2008-2019 Sarah Walker. * Copyright 2016-2019 Miran Grca. */ #include #include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include "cpu.h" #include <86box/device.h> #include <86box/machine.h> #include <86box/timer.h> #include <86box/io.h> #include <86box/pit.h> #include <86box/mem.h> #include <86box/rom.h> #include <86box/plat.h> #include <86box/ui.h> #include <86box/video.h> #include <86box/vid_8514a.h> #include <86box/vid_xga.h> #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> #include <86box/vid_xga_device.h> void svga_doblit(int wx, int wy, svga_t *svga); void svga_poll(void *priv); svga_t *svga_8514; extern int cyc_total; extern uint8_t edatlookup[4][4]; uint8_t svga_rotate[8][256]; /*Primary SVGA device. As multiple video cards are not yet supported this is the only SVGA device.*/ static svga_t *svga_pri; #ifdef ENABLE_SVGA_LOG int svga_do_log = ENABLE_SVGA_LOG; static void svga_log(const char *fmt, ...) { va_list ap; if (svga_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else # define svga_log(fmt, ...) #endif svga_t * svga_get_pri(void) { return svga_pri; } void svga_set_override(svga_t *svga, int val) { ibm8514_t *dev = (ibm8514_t *) svga->dev8514; if (svga->override && !val) svga->fullchange = svga->monitor->mon_changeframecount; svga->override = val; svga_log("Override=%x.\n", val); if (ibm8514_active && (svga->dev8514 != NULL)) { if (dev->on) { if (svga->override) timer_set_callback(&svga->timer, svga_poll); else timer_set_callback(&svga->timer, ibm8514_poll); } else timer_set_callback(&svga->timer, svga_poll); } else timer_set_callback(&svga->timer, svga_poll); #ifdef OVERRIDE_OVERSCAN if (!val) { /* Override turned off, restore overscan X and Y per the CRTC. */ svga->monitor->mon_overscan_y = (svga->rowcount + 1) << 1; if (svga->monitor->mon_overscan_y < 16) svga->monitor->mon_overscan_y = 16; svga->monitor->mon_overscan_x = (svga->seqregs[1] & 1) ? 16 : 18; if (svga->seqregs[1] & 8) svga->monitor->mon_overscan_x <<= 1; } else svga->monitor->mon_overscan_x = svga->monitor->mon_overscan_y = 16; /* Override turned off, fix overcan X and Y to 16. */ #endif } void svga_out(uint16_t addr, uint8_t val, void *priv) { svga_t *svga = (svga_t *) priv; ibm8514_t *dev = (ibm8514_t *) svga->dev8514; xga_t *xga = (xga_t *) svga->xga; uint8_t o; uint8_t index; uint8_t pal4to16[16] = { 0, 7, 0x38, 0x3f, 0, 3, 4, 0x3f, 0, 2, 4, 0x3e, 0, 3, 5, 0x3f }; if ((addr >= 0x2ea) && (addr <= 0x2ed)) { if (!dev) return; } switch (addr) { case 0x2ea: dev->dac_mask = val; break; case 0x2eb: case 0x2ec: dev->dac_pos = 0; dev->dac_status = addr & 0x03; dev->dac_addr = (val + (addr & 0x01)) & 0xff; break; case 0x2ed: svga->fullchange = svga->monitor->mon_changeframecount; switch (dev->dac_pos) { case 0: dev->dac_r = val; dev->dac_pos++; break; case 1: dev->dac_g = val; dev->dac_pos++; break; case 2: index = dev->dac_addr & 0xff; dev->dac_b = val; dev->_8514pal[index].r = dev->dac_r; dev->_8514pal[index].g = dev->dac_g; dev->_8514pal[index].b = dev->dac_b; if (svga->ramdac_type == RAMDAC_8BIT) dev->pallook[index] = makecol32(dev->_8514pal[index].r, dev->_8514pal[index].g, dev->_8514pal[index].b); else dev->pallook[index] = makecol32(video_6to8[dev->_8514pal[index].r & 0x3f], video_6to8[dev->_8514pal[index].g & 0x3f], video_6to8[dev->_8514pal[index].b & 0x3f]); dev->dac_pos = 0; dev->dac_addr = (dev->dac_addr + 1) & 0xff; break; default: break; } break; case 0x3c0: case 0x3c1: if (!svga->attrff) { svga->attraddr = val & 0x1f; if ((val & 0x20) != svga->attr_palette_enable) { svga->fullchange = 3; svga->attr_palette_enable = val & 0x20; svga_log("Write Port %03x palette enable=%02x.\n", addr, svga->attr_palette_enable); svga_recalctimings(svga); } } else { if ((svga->attraddr == 0x13) && (svga->attrregs[0x13] != val)) svga->fullchange = svga->monitor->mon_changeframecount; o = svga->attrregs[svga->attraddr & 0x1f]; svga->attrregs[svga->attraddr & 0x1f] = val; if (svga->attraddr < 0x10) svga->fullchange = svga->monitor->mon_changeframecount; if ((svga->attraddr == 0x10) || (svga->attraddr == 0x14) || (svga->attraddr < 0x10)) { for (int c = 0; c < 0x10; c++) { if (svga->attrregs[0x10] & 0x80) svga->egapal[c] = (svga->attrregs[c] & 0xf) | ((svga->attrregs[0x14] & 0xf) << 4); else if (svga->ati_4color) svga->egapal[c] = pal4to16[(c & 0x03) | ((val >> 2) & 0xc)]; else svga->egapal[c] = (svga->attrregs[c] & 0x3f) | ((svga->attrregs[0x14] & 0xc) << 4); } svga->fullchange = svga->monitor->mon_changeframecount; } /* Recalculate timings on change of attribute register 0x11 (overscan border color) too. */ if (svga->attraddr == 0x10) { if (o != val) { svga_log("ATTR10.\n"); svga_recalctimings(svga); } } else if (svga->attraddr == 0x11) { svga->overscan_color = svga->pallook[svga->attrregs[0x11]]; if (o != val) { svga_log("ATTR11.\n"); svga_recalctimings(svga); } } else if (svga->attraddr == 0x12) { if ((val & 0xf) != svga->plane_mask) svga->fullchange = svga->monitor->mon_changeframecount; svga->plane_mask = val & 0xf; } } svga->attrff ^= 1; break; case 0x3c2: svga->miscout = val; svga->vidclock = val & 4; io_removehandler(0x03a0, 0x0020, svga->video_in, NULL, NULL, svga->video_out, NULL, NULL, svga->priv); if (!(val & 1)) io_sethandler(0x03a0, 0x0020, svga->video_in, NULL, NULL, svga->video_out, NULL, NULL, svga->priv); svga_recalctimings(svga); break; case 0x3c3: if (xga_active && xga) xga->on = (val & 0x01) ? 0 : 1; if (ibm8514_active && dev) dev->on = (val & 0x01) ? 0 : 1; svga_log("Write Port 3C3.\n"); svga_recalctimings(svga); break; case 0x3c4: svga->seqaddr = val; break; case 0x3c5: if (svga->seqaddr > 0xf) return; o = svga->seqregs[svga->seqaddr & 0xf]; svga->seqregs[svga->seqaddr & 0xf] = val; if (o != val && (svga->seqaddr & 0xf) == 1) { svga_log("SEQADDR1 write1.\n"); svga_recalctimings(svga); } switch (svga->seqaddr & 0xf) { case 1: if (svga->scrblank && !(val & 0x20)) svga->fullchange = 3; svga->scrblank = (svga->scrblank & ~0x20) | (val & 0x20); svga_log("SEQADDR1 write2.\n"); svga_recalctimings(svga); break; case 2: svga->writemask = val & 0xf; break; case 3: svga->charsetb = (((val >> 2) & 3) * 0x10000) + 2; svga->charseta = ((val & 3) * 0x10000) + 2; if (val & 0x10) svga->charseta += 0x8000; if (val & 0x20) svga->charsetb += 0x8000; break; case 4: svga->chain2_write = !(val & 4); svga->chain4 = (svga->chain4 & ~8) | (val & 8); svga->fast = (svga->gdcreg[8] == 0xff && !(svga->gdcreg[3] & 0x18) && !svga->gdcreg[1]) && ((svga->chain4 && (svga->packed_chain4 || svga->force_old_addr)) || svga->fb_only) && !(svga->adv_flags & FLAG_ADDR_BY8); break; default: break; } break; case 0x3c6: svga->dac_mask = val; break; case 0x3c7: case 0x3c8: svga->dac_pos = 0; svga->dac_status = addr & 0x03; svga->dac_addr = (val + (addr & 0x01)) & 0xff; break; case 0x3c9: if (svga->adv_flags & FLAG_RAMDAC_SHIFT) val <<= 2; svga->fullchange = svga->monitor->mon_changeframecount; switch (svga->dac_pos) { case 0: svga->dac_r = val; svga->dac_pos++; break; case 1: svga->dac_g = val; svga->dac_pos++; break; case 2: index = svga->dac_addr & 0xff; svga->dac_b = val; svga->vgapal[index].r = svga->dac_r; svga->vgapal[index].g = svga->dac_g; svga->vgapal[index].b = svga->dac_b; if (svga->ramdac_type == RAMDAC_8BIT) svga->pallook[index] = makecol32(svga->vgapal[index].r, svga->vgapal[index].g, svga->vgapal[index].b); else svga->pallook[index] = makecol32(video_6to8[svga->vgapal[index].r & 0x3f], video_6to8[svga->vgapal[index].g & 0x3f], video_6to8[svga->vgapal[index].b & 0x3f]); svga->dac_pos = 0; svga->dac_addr = (svga->dac_addr + 1) & 0xff; break; default: break; } break; case 0x3ce: svga->gdcaddr = val; break; case 0x3cf: o = svga->gdcreg[svga->gdcaddr & 15]; switch (svga->gdcaddr & 15) { case 2: svga->colourcompare = val; break; case 4: svga->readplane = val & 3; break; case 5: svga->writemode = val & 3; svga->readmode = val & 8; svga->chain2_read = val & 0x10; break; case 6: if ((svga->gdcreg[6] & 0xc) != (val & 0xc)) { switch (val & 0xc) { case 0x0: /*128k at A0000*/ mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x20000); svga->banked_mask = 0xffff; break; case 0x4: /*64k at A0000*/ mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x10000); svga->banked_mask = 0xffff; break; case 0x8: /*32k at B0000*/ mem_mapping_set_addr(&svga->mapping, 0xb0000, 0x08000); svga->banked_mask = 0x7fff; break; case 0xC: /*32k at B8000*/ mem_mapping_set_addr(&svga->mapping, 0xb8000, 0x08000); svga->banked_mask = 0x7fff; break; default: break; } } break; case 7: svga->colournocare = val; break; default: break; } svga->gdcreg[svga->gdcaddr & 15] = val; svga->fast = (svga->gdcreg[8] == 0xff && !(svga->gdcreg[3] & 0x18) && !svga->gdcreg[1]) && ((svga->chain4 && (svga->packed_chain4 || svga->force_old_addr)) || svga->fb_only); if (((svga->gdcaddr & 15) == 5 && (val ^ o) & 0x70) || ((svga->gdcaddr & 15) == 6 && (val ^ o) & 1)) { svga_log("GDCADDR%02x recalc.\n", svga->gdcaddr & 0x0f); svga_recalctimings(svga); } break; case 0x3da: svga->fcr = val; break; default: break; } } uint8_t svga_in(uint16_t addr, void *priv) { svga_t *svga = (svga_t *) priv; ibm8514_t *dev = (ibm8514_t *) svga->dev8514; xga_t *xga = (xga_t *) svga->xga; uint8_t index; uint8_t ret = 0xff; if ((addr >= 0x2ea) && (addr <= 0x2ed)) { if (!dev) return ret; } switch (addr) { case 0x2ea: ret = dev->dac_mask; break; case 0x2eb: ret = dev->dac_status; break; case 0x2ec: ret = dev->dac_addr; break; case 0x2ed: index = (dev->dac_addr - 1) & 0xff; switch (dev->dac_pos) { case 0: dev->dac_pos++; if (svga->ramdac_type == RAMDAC_8BIT) ret = dev->_8514pal[index].r; else ret = dev->_8514pal[index].r & 0x3f; break; case 1: dev->dac_pos++; if (svga->ramdac_type == RAMDAC_8BIT) ret = dev->_8514pal[index].g; else ret = dev->_8514pal[index].g & 0x3f; break; case 2: dev->dac_pos = 0; dev->dac_addr = (dev->dac_addr + 1) & 0xff; if (svga->ramdac_type == RAMDAC_8BIT) ret = dev->_8514pal[index].b; else ret = dev->_8514pal[index].b & 0x3f; break; default: break; } break; case 0x3c0: ret = svga->attraddr | svga->attr_palette_enable; break; case 0x3c1: ret = svga->attrregs[svga->attraddr]; break; case 0x3c2: if ((svga->vgapal[0].r + svga->vgapal[0].g + svga->vgapal[0].b) >= 0x4e) ret = 0; else ret = 0x10; break; case 0x3c3: ret = 0x00; if (xga_active && xga) ret |= !xga->on; if (ibm8514_active && dev) ret |= !dev->on; break; case 0x3c4: ret = svga->seqaddr; break; case 0x3c5: ret = svga->seqregs[svga->seqaddr & 0x0f]; break; case 0x3c6: ret = svga->dac_mask; break; case 0x3c7: ret = svga->dac_status; break; case 0x3c8: ret = svga->dac_addr; break; case 0x3c9: index = (svga->dac_addr - 1) & 0xff; switch (svga->dac_pos) { case 0: svga->dac_pos++; if (svga->ramdac_type == RAMDAC_8BIT) ret = svga->vgapal[index].r; else ret = svga->vgapal[index].r & 0x3f; break; case 1: svga->dac_pos++; if (svga->ramdac_type == RAMDAC_8BIT) ret = svga->vgapal[index].g; else ret = svga->vgapal[index].g & 0x3f; break; case 2: svga->dac_pos = 0; svga->dac_addr = (svga->dac_addr + 1) & 0xff; if (svga->ramdac_type == RAMDAC_8BIT) ret = svga->vgapal[index].b; else ret = svga->vgapal[index].b & 0x3f; break; default: break; } if (svga->adv_flags & FLAG_RAMDAC_SHIFT) ret >>= 2; break; case 0x3ca: ret = svga->fcr; break; case 0x3cc: ret = svga->miscout; break; case 0x3ce: ret = svga->gdcaddr; break; case 0x3cf: /* The spec says GDC addresses 0xF8 to 0xFB return the latch. */ switch (svga->gdcaddr) { case 0xf8: ret = svga->latch.b[0]; break; case 0xf9: ret = svga->latch.b[1]; break; case 0xfa: ret = svga->latch.b[2]; break; case 0xfb: ret = svga->latch.b[3]; break; default: ret = svga->gdcreg[svga->gdcaddr & 0xf]; break; } break; case 0x3da: svga->attrff = 0; if (svga->cgastat & 0x01) svga->cgastat &= ~0x30; else svga->cgastat ^= 0x30; ret = svga->cgastat; if ((svga->fcr & 0x08) && svga->dispon) ret |= 0x08; break; default: break; } if ((addr >= 0x3c6) && (addr <= 0x3c9)) svga_log("VGA IN addr=%03x, temp=%02x.\n", addr, ret); return ret; } void svga_set_ramdac_type(svga_t *svga, int type) { ibm8514_t *dev = (ibm8514_t *) svga->dev8514; xga_t *xga = (xga_t *) svga->xga; if (svga->ramdac_type != type) { svga->ramdac_type = type; for (int c = 0; c < 256; c++) { if (ibm8514_active && dev) { if (svga->ramdac_type == RAMDAC_8BIT) dev->pallook[c] = makecol32(dev->_8514pal[c].r, dev->_8514pal[c].g, dev->_8514pal[c].b); else dev->pallook[c] = makecol32((dev->_8514pal[c].r & 0x3f) * 4, (dev->_8514pal[c].g & 0x3f) * 4, (dev->_8514pal[c].b & 0x3f) * 4); } if (xga_active && xga) { if (svga->ramdac_type == RAMDAC_8BIT) xga->pallook[c] = makecol32(xga->xgapal[c].r, xga->xgapal[c].g, xga->xgapal[c].b); else { xga->pallook[c] = makecol32((xga->xgapal[c].r & 0x3f) * 4, (xga->xgapal[c].g & 0x3f) * 4, (xga->xgapal[c].b & 0x3f) * 4); } } if (svga->ramdac_type == RAMDAC_8BIT) svga->pallook[c] = makecol32(svga->vgapal[c].r, svga->vgapal[c].g, svga->vgapal[c].b); else svga->pallook[c] = makecol32((svga->vgapal[c].r & 0x3f) * 4, (svga->vgapal[c].g & 0x3f) * 4, (svga->vgapal[c].b & 0x3f) * 4); } } } void svga_recalctimings(svga_t *svga) { ibm8514_t *dev = (ibm8514_t *) svga->dev8514; double crtcconst; double _dispontime; double _dispofftime; double disptime; double crtcconst8514 = 0.0; double _dispontime8514 = 0.0; double _dispofftime8514 = 0.0; double disptime8514 = 0.0; #ifdef ENABLE_SVGA_LOG int vsyncend; int vblankend; int hdispstart; int hdispend; int hsyncstart; int hsyncend; #endif svga->vtotal = svga->crtc[6]; svga->dispend = svga->crtc[0x12]; svga->vsyncstart = svga->crtc[0x10]; svga->split = svga->crtc[0x18]; svga->vblankstart = svga->crtc[0x15]; if (svga->crtc[7] & 1) svga->vtotal |= 0x100; if (svga->crtc[7] & 32) svga->vtotal |= 0x200; svga->vtotal += 2; if (svga->crtc[7] & 2) svga->dispend |= 0x100; if (svga->crtc[7] & 64) svga->dispend |= 0x200; svga->dispend++; if (svga->crtc[7] & 4) svga->vsyncstart |= 0x100; if (svga->crtc[7] & 128) svga->vsyncstart |= 0x200; svga->vsyncstart++; if (svga->crtc[7] & 0x10) svga->split |= 0x100; if (svga->crtc[9] & 0x40) svga->split |= 0x200; svga->split++; if (svga->crtc[7] & 0x08) svga->vblankstart |= 0x100; if (svga->crtc[9] & 0x20) svga->vblankstart |= 0x200; svga->vblankstart++; svga->hdisp = svga->crtc[1]; svga->hdisp++; svga->htotal = svga->crtc[0]; /* +5 has been verified by Sergi to be correct - +6 must have been an off by one error. */ svga->htotal += 5; /*+5 is required for Tyrian*/ svga->rowoffset = svga->crtc[0x13]; svga->clock = (svga->vidclock) ? VGACONST2 : VGACONST1; svga->lowres = !!(svga->attrregs[0x10] & 0x40); svga->interlace = 0; svga->ma_latch = ((svga->crtc[0xc] << 8) | svga->crtc[0xd]) + ((svga->crtc[8] & 0x60) >> 5); svga->ca_adj = 0; svga->rowcount = svga->crtc[9] & 0x1f; svga->hdisp_time = svga->hdisp; svga->render = svga_render_blank; if (!svga->scrblank && (svga->crtc[0x17] & 0x80) && svga->attr_palette_enable) { /* TODO: In case of bug reports, disable 9-dots-wide character clocks in graphics modes. */ if (!(svga->gdcreg[6] & 1) && !(svga->attrregs[0x10] & 1)) { if (svga->seqregs[1] & 8) svga->hdisp *= (svga->seqregs[1] & 1) ? 16 : 18; else svga->hdisp *= (svga->seqregs[1] & 1) ? 8 : 9; } else { if (svga->seqregs[1] & 8) svga->hdisp *= 16; else svga->hdisp *= 8; } if (!(svga->gdcreg[6] & 1) && !(svga->attrregs[0x10] & 1)) { /*Text mode*/ if (svga->seqregs[1] & 8) { /*40 column*/ svga->render = svga_render_text_40; } else svga->render = svga_render_text_80; svga->hdisp_old = svga->hdisp; } else { svga->hdisp_old = svga->hdisp; if ((svga->bpp <= 8) || ((svga->gdcreg[5] & 0x60) <= 0x20)) { if ((svga->gdcreg[5] & 0x60) == 0x00) { if (svga->seqregs[1] & 8) /*Low res (320)*/ svga->render = svga_render_4bpp_lowres; else svga->render = svga_render_4bpp_highres; } else if ((svga->gdcreg[5] & 0x60) == 0x20) { if (svga->seqregs[1] & 8) { /*Low res (320)*/ svga->render = svga_render_2bpp_lowres; svga_log("2 bpp low res\n"); } else svga->render = svga_render_2bpp_highres; } else { svga->map8 = svga->pallook; if (svga->lowres) /*Low res (320)*/ svga->render = svga_render_8bpp_lowres; else svga->render = svga_render_8bpp_highres; } } else { switch (svga->gdcreg[5] & 0x60) { case 0x40: case 0x60: /*256+ colours*/ switch (svga->bpp) { case 15: if (svga->lowres) svga->render = svga_render_15bpp_lowres; else svga->render = svga_render_15bpp_highres; break; case 16: if (svga->lowres) svga->render = svga_render_16bpp_lowres; else svga->render = svga_render_16bpp_highres; break; case 17: if (svga->lowres) svga->render = svga_render_15bpp_mix_lowres; else svga->render = svga_render_15bpp_mix_highres; break; case 24: if (svga->lowres) svga->render = svga_render_24bpp_lowres; else svga->render = svga_render_24bpp_highres; break; case 32: if (svga->lowres) svga->render = svga_render_32bpp_lowres; else svga->render = svga_render_32bpp_highres; break; default: break; } break; default: break; } } } } svga->linedbl = svga->crtc[9] & 0x80; svga->char_width = (svga->seqregs[1] & 1) ? 8 : 9; svga->monitor->mon_overscan_y = (svga->rowcount + 1) << 1; if (svga->monitor->mon_overscan_y < 16) svga->monitor->mon_overscan_y = 16; if (!(svga->gdcreg[6] & 1) && !(svga->attrregs[0x10] & 1)) { svga->monitor->mon_overscan_x = (svga->seqregs[1] & 1) ? 16 : 18; if (svga->seqregs[1] & 8) svga->monitor->mon_overscan_x <<= 1; } else svga->monitor->mon_overscan_x = 16; svga->hblankstart = svga->crtc[2]; svga->hblank_end_val = (svga->crtc[3] & 0x1f) | ((svga->crtc[5] & 0x80) ? 0x20 : 0x00); svga->hblank_end_mask = 0x0000003f; svga_log("htotal = %i, hblankstart = %i, hblank_end_val = %02X\n", svga->htotal, svga->hblankstart, svga->hblank_end_val); if (!svga->scrblank && svga->attr_palette_enable) { /* TODO: In case of bug reports, disable 9-dots-wide character clocks in graphics modes. */ if (!(svga->gdcreg[6] & 1) && !(svga->attrregs[0x10] & 1)) { if (svga->seqregs[1] & 8) svga->dots_per_clock = ((svga->seqregs[1] & 1) ? 16 : 18); else svga->dots_per_clock = ((svga->seqregs[1] & 1) ? 8 : 9); } else { if (svga->seqregs[1] & 8) svga->dots_per_clock = 16; else svga->dots_per_clock = 8; } } else svga->dots_per_clock = 1; svga->multiplier = 1.0; if (svga->recalctimings_ex) svga->recalctimings_ex(svga); if (ibm8514_active && (svga->dev8514 != NULL)) { if ((dev->local & 0xff) == 0x00) ibm8514_recalctimings(svga); } if (xga_active && (svga->xga != NULL)) xga_recalctimings(svga); if (!svga->hoverride) { uint32_t dot = svga->hblankstart; uint32_t adj_dot = svga->hblankstart; /* Verified with both the Voodoo 3 and the S3 cards: compare 7 bits if bit 7 is set, otherwise compare 6 bits. */ uint32_t eff_mask = (svga->hblank_end_val & ~0x0000003f) ? svga->hblank_end_mask : 0x0000003f; svga->hblank_sub = 0; svga_log("HDISP=%d, CRTC1+1=%d, Blank: %04i-%04i, Total: %04i, Mask: %02X, ADJ_DOT=%04i.\n", svga->hdisp, svga->crtc[1] + 1, svga->hblankstart, svga->hblank_end_val, svga->htotal, eff_mask, adj_dot); while (adj_dot < (svga->htotal << 1)) { if (dot == svga->htotal) dot = 0; if (adj_dot >= svga->htotal) svga->hblank_sub++; svga_log("Loop: adjdot=%d, htotal=%d, dotmask=%02x, hblankendvalmask=%02x, blankendval=%02x.\n", adj_dot, svga->htotal, dot & eff_mask, svga->hblank_end_val & eff_mask, svga->hblank_end_val); if ((dot & eff_mask) == (svga->hblank_end_val & eff_mask)) break; dot++; adj_dot++; } svga->hdisp -= (svga->hblank_sub * svga->dots_per_clock); } #ifdef TBD if (ibm8514_active && (svga->dev8514 != NULL)) { if (dev->on) { uint32_t dot8514 = dev->h_blankstart; uint32_t adj_dot8514 = dev->h_blankstart; uint32_t eff_mask8514 = 0x0000003f; dev->hblank_sub = 0; while (adj_dot8514 < (dev->h_total << 1)) { if (dot8514 == dev->h_total) dot8514 = 0; if (adj_dot8514 >= dev->h_total) dev->hblank_sub++; if ((dot8514 & eff_mask8514) == (dev->h_blank_end_val & eff_mask8514)) break; dot8514++; adj_dot8514++; } dev->h_disp -= dev->hblank_sub; } } #endif if (svga->hdisp >= 2048) svga->monitor->mon_overscan_x = 0; svga->y_add = (svga->monitor->mon_overscan_y >> 1); svga->x_add = (svga->monitor->mon_overscan_x >> 1); if (svga->vblankstart < svga->dispend) { svga_log("DISPEND > VBLANKSTART.\n"); svga->dispend = svga->vblankstart; } crtcconst = svga->clock * svga->char_width; if (ibm8514_active && (svga->dev8514 != NULL)) { if (dev->on) crtcconst8514 = svga->clock8514; } #ifdef ENABLE_SVGA_LOG vsyncend = (svga->vsyncstart & 0xfffffff0) | (svga->crtc[0x11] & 0x0f); if (vsyncend <= svga->vsyncstart) vsyncend += 0x00000010; vblankend = (svga->vblankstart & 0xffffff80) | (svga->crtc[0x16] & 0x7f); if (vblankend <= svga->vblankstart) vblankend += 0x00000080; hdispstart = ((svga->crtc[3] >> 5) & 3); hdispend = svga->crtc[1] + 1; hsyncstart = svga->crtc[4] + ((svga->crtc[5] >> 5) & 3) + 1; hsyncend = (hsyncstart & 0xffffffe0) | (svga->crtc[5] & 0x1f); if (hsyncend <= hsyncstart) hsyncend += 0x00000020; #endif svga_log("Last scanline in the vertical period: %i\n" "First scanline after the last of active display: %i\n" "First scanline with vertical retrace asserted: %i\n" "First scanline after the last with vertical retrace asserted: %i\n" "First scanline of blanking: %i\n" "First scanline after the last of blanking: %i\n" "\n" "Last character in the horizontal period: %i\n" "First character of active display: %i\n" "First character after the last of active display: %i\n" "First character with horizontal retrace asserted: %i\n" "First character after the last with horizontal retrace asserted: %i\n" "First character of blanking: %i\n" "First character after the last of blanking: %i\n" "\n" "\n", svga->vtotal, svga->dispend, svga->vsyncstart, vsyncend, svga->vblankstart, vblankend, svga->htotal, hdispstart, hdispend, hsyncstart, hsyncend, svga->hblankstart, svga->hblankend); disptime = svga->htotal * svga->multiplier; _dispontime = svga->hdisp_time; if (ibm8514_active && (svga->dev8514 != NULL)) { if (dev->on) { disptime8514 = dev->h_total ? dev->h_total : TIMER_USEC; _dispontime8514 = dev->hdisped; } } if (svga->seqregs[1] & 8) { disptime *= 2; _dispontime *= 2; } _dispofftime = disptime - _dispontime; _dispontime *= crtcconst; _dispofftime *= crtcconst; svga->dispontime = (uint64_t) (_dispontime); svga->dispofftime = (uint64_t) (_dispofftime); if (svga->dispontime < TIMER_USEC) svga->dispontime = TIMER_USEC; if (svga->dispofftime < TIMER_USEC) svga->dispofftime = TIMER_USEC; if (ibm8514_active && (svga->dev8514 != NULL)) { if (dev->on) { _dispofftime8514 = disptime8514 - _dispontime8514; _dispontime8514 *= crtcconst8514; _dispofftime8514 *= crtcconst8514; dev->dispontime = (uint64_t) (_dispontime8514); dev->dispofftime = (uint64_t) (_dispofftime8514); if (dev->dispontime < TIMER_USEC) dev->dispontime = TIMER_USEC; if (dev->dispofftime < TIMER_USEC) dev->dispofftime = TIMER_USEC; svga_log("IBM 8514/A poll.\n"); timer_set_callback(&svga->timer, ibm8514_poll); } else { svga_log("SVGA Poll.\n"); timer_set_callback(&svga->timer, svga_poll); } } if (!svga->force_old_addr) svga_recalc_remap_func(svga); /* Inform the user interface of any DPMS mode changes. */ if (svga->dpms) { if (!svga->dpms_ui) { /* Make sure to black out the entire screen to avoid lingering image. */ int y_add = enable_overscan ? svga->monitor->mon_overscan_y : 0; int x_add = enable_overscan ? svga->monitor->mon_overscan_x : 0; int y_start = enable_overscan ? 0 : (svga->monitor->mon_overscan_y >> 1); int x_start = enable_overscan ? 0 : (svga->monitor->mon_overscan_x >> 1); video_wait_for_buffer_monitor(svga->monitor_index); memset(svga->monitor->target_buffer->dat, 0, svga->monitor->target_buffer->w * svga->monitor->target_buffer->h * 4); video_blit_memtoscreen_monitor(x_start, y_start, svga->monitor->mon_xsize + x_add, svga->monitor->mon_ysize + y_add, svga->monitor_index); video_wait_for_buffer_monitor(svga->monitor_index); svga->dpms_ui = 1; ui_sb_set_text_w(plat_get_string(STRING_MONITOR_SLEEP)); } } else if (svga->dpms_ui) { svga->dpms_ui = 0; ui_sb_set_text_w(NULL); } } static void svga_do_render(svga_t *svga) { /* Always render a blank screen and nothing else while in DPMS mode. */ if (svga->dpms) { svga_render_blank(svga); return; } if (!svga->override) { svga->render(svga); svga->x_add = (svga->monitor->mon_overscan_x >> 1); svga_render_overscan_left(svga); svga_render_overscan_right(svga); svga->x_add = (svga->monitor->mon_overscan_x >> 1) - svga->scrollcache; } if (svga->overlay_on) { if (!svga->override && svga->overlay_draw) svga->overlay_draw(svga, svga->displine + svga->y_add); svga->overlay_on--; if (svga->overlay_on && svga->interlace) svga->overlay_on--; } if (svga->dac_hwcursor_on) { if (!svga->override && svga->dac_hwcursor_draw) svga->dac_hwcursor_draw(svga, (svga->displine + svga->y_add + ((svga->dac_hwcursor_latch.y >= 0) ? 0 : svga->dac_hwcursor_latch.y)) & 2047); svga->dac_hwcursor_on--; if (svga->dac_hwcursor_on && svga->interlace) svga->dac_hwcursor_on--; } if (svga->hwcursor_on) { if (!svga->override && svga->hwcursor_draw) svga->hwcursor_draw(svga, (svga->displine + svga->y_add + ((svga->hwcursor_latch.y >= 0) ? 0 : svga->hwcursor_latch.y)) & 2047); svga->hwcursor_on--; if (svga->hwcursor_on && svga->interlace) svga->hwcursor_on--; } } void svga_poll(void *priv) { svga_t *svga = (svga_t *) priv; xga_t *xga = (xga_t *) svga->xga; uint32_t x; uint32_t blink_delay; int wx; int wy; int ret; int old_ma; if (!svga->override) { if (xga_active && xga && xga->on) { if ((xga->disp_cntl_2 & 7) >= 2) { xga_poll(svga); return; } } } if (!svga->linepos) { if (svga->displine == ((svga->hwcursor_latch.y < 0) ? 0 : svga->hwcursor_latch.y) && svga->hwcursor_latch.ena) { svga->hwcursor_on = svga->hwcursor_latch.cur_ysize - svga->hwcursor_latch.yoff; svga->hwcursor_oddeven = 0; } if (svga->displine == (((svga->hwcursor_latch.y < 0) ? 0 : svga->hwcursor_latch.y) + 1) && svga->hwcursor_latch.ena && svga->interlace) { svga->hwcursor_on = svga->hwcursor_latch.cur_ysize - (svga->hwcursor_latch.yoff + 1); svga->hwcursor_oddeven = 1; } if (svga->displine == ((svga->dac_hwcursor_latch.y < 0) ? 0 : svga->dac_hwcursor_latch.y) && svga->dac_hwcursor_latch.ena) { svga->dac_hwcursor_on = svga->dac_hwcursor_latch.cur_ysize - svga->dac_hwcursor_latch.yoff; svga->dac_hwcursor_oddeven = 0; } if (svga->displine == (((svga->dac_hwcursor_latch.y < 0) ? 0 : svga->dac_hwcursor_latch.y) + 1) && svga->dac_hwcursor_latch.ena && svga->interlace) { svga->dac_hwcursor_on = svga->dac_hwcursor_latch.cur_ysize - (svga->dac_hwcursor_latch.yoff + 1); svga->dac_hwcursor_oddeven = 1; } if (svga->displine == svga->overlay_latch.y && svga->overlay_latch.ena) { svga->overlay_on = svga->overlay_latch.cur_ysize - svga->overlay_latch.yoff; svga->overlay_oddeven = 0; } if (svga->displine == svga->overlay_latch.y + 1 && svga->overlay_latch.ena && svga->interlace) { svga->overlay_on = svga->overlay_latch.cur_ysize - svga->overlay_latch.yoff; svga->overlay_oddeven = 1; } timer_advance_u64(&svga->timer, svga->dispofftime); svga->cgastat |= 1; svga->linepos = 1; if (svga->dispon) { svga->hdisp_on = 1; svga->ma &= svga->vram_display_mask; if (svga->firstline == 2000) { svga->firstline = svga->displine; video_wait_for_buffer_monitor(svga->monitor_index); } if (svga->hwcursor_on || svga->dac_hwcursor_on || svga->overlay_on) svga->changedvram[svga->ma >> 12] = svga->changedvram[(svga->ma >> 12) + 1] = svga->interlace ? 3 : 2; if (svga->vertical_linedbl) { old_ma = svga->ma; svga->displine <<= 1; svga->y_add <<= 1; svga_do_render(svga); svga->displine++; svga->ma = old_ma; svga_do_render(svga); svga->y_add >>= 1; svga->displine >>= 1; } else svga_do_render(svga); if (svga->lastline < svga->displine) svga->lastline = svga->displine; } svga->displine++; if (svga->interlace) svga->displine++; if ((svga->cgastat & 8) && ((svga->displine & 15) == (svga->crtc[0x11] & 15)) && svga->vslines) svga->cgastat &= ~8; svga->vslines++; if (svga->displine > 2000) svga->displine = 0; } else { timer_advance_u64(&svga->timer, svga->dispontime); if (svga->dispon) svga->cgastat &= ~1; svga->hdisp_on = 0; svga->linepos = 0; if ((svga->sc == (svga->crtc[11] & 31)) || (svga->sc == svga->rowcount)) svga->con = 0; if (svga->dispon) { /* TODO: Verify real hardware behaviour for out-of-range fine vertical scroll - S3 Trio64V2/DX: sc == rowcount, wrapping 5-bit counter. */ if (svga->linedbl && !svga->linecountff) { svga->linecountff = 1; svga->ma = svga->maback; } else if (svga->sc == svga->rowcount) { svga->linecountff = 0; svga->sc = 0; svga->maback += (svga->adv_flags & FLAG_NO_SHIFT3) ? svga->rowoffset : (svga->rowoffset << 3); if (svga->interlace) svga->maback += (svga->adv_flags & FLAG_NO_SHIFT3) ? svga->rowoffset : (svga->rowoffset << 3); svga->maback &= svga->vram_display_mask; svga->ma = svga->maback; } else { svga->linecountff = 0; svga->sc++; svga->sc &= 0x1f; svga->ma = svga->maback; } } svga->hsync_divisor ^= 1; if (svga->hsync_divisor && (svga->crtc[0x17] & 4)) return; svga->vc++; svga->vc &= 0x7ff; if (svga->vc == svga->split) { ret = 1; if (svga->line_compare) ret = svga->line_compare(svga); if (ret) { if (svga->interlace && svga->oddeven) svga->ma = svga->maback = (svga->rowoffset << 1) + svga->hblank_sub; else svga->ma = svga->maback = svga->hblank_sub; svga->ma = (svga->ma << 2); svga->maback = (svga->maback << 2); svga->sc = 0; if (svga->attrregs[0x10] & 0x20) { svga->scrollcache = 0; svga->x_add = (svga->monitor->mon_overscan_x >> 1); } } } if (svga->vc == svga->dispend) { if (svga->vblank_start) svga->vblank_start(svga); svga->dispon = 0; blink_delay = (svga->crtc[11] & 0x60) >> 5; if (svga->crtc[10] & 0x20) svga->cursoron = 0; else if (blink_delay == 2) svga->cursoron = ((svga->blink % 96) >= 48); else svga->cursoron = svga->blink & (16 + (16 * blink_delay)); if (!(svga->blink & 15)) svga->fullchange = 2; svga->blink = (svga->blink + 1) & 0x7f; for (x = 0; x < ((svga->vram_mask + 1) >> 12); x++) { if (svga->changedvram[x]) svga->changedvram[x]--; } if (svga->fullchange) svga->fullchange--; } if (svga->vc == svga->vsyncstart) { svga->dispon = 0; svga->cgastat |= 8; x = svga->hdisp; if (svga->interlace && !svga->oddeven) svga->lastline++; if (svga->interlace && svga->oddeven) svga->firstline--; wx = x; if (!svga->override) { if (svga->vertical_linedbl) { wy = (svga->lastline - svga->firstline) << 1; svga->vdisp = wy + 1; svga_doblit(wx, wy, svga); } else { wy = svga->lastline - svga->firstline; svga->vdisp = wy + 1; svga_doblit(wx, wy, svga); } } svga->firstline = 2000; svga->lastline = 0; svga->firstline_draw = 2000; svga->lastline_draw = 0; svga->oddeven ^= 1; svga->monitor->mon_changeframecount = svga->interlace ? 3 : 2; svga->vslines = 0; if (svga->interlace && svga->oddeven) svga->ma = svga->maback = svga->ma_latch + (svga->rowoffset << 1) + svga->hblank_sub; else svga->ma = svga->maback = svga->ma_latch + svga->hblank_sub; svga->ca = ((svga->crtc[0xe] << 8) | svga->crtc[0xf]) + ((svga->crtc[0xb] & 0x60) >> 5) + svga->ca_adj; if (!(svga->adv_flags & FLAG_NO_SHIFT3)) { svga->ma = (svga->ma << 2); svga->maback = (svga->maback << 2); } svga->ca = (svga->ca << 2); if (svga->vsync_callback) svga->vsync_callback(svga); } #if 0 if (svga->vc == lines_num) { #endif if (svga->vc == svga->vtotal) { svga->vc = 0; svga->sc = (svga->crtc[0x8] & 0x1f); svga->dispon = 1; svga->displine = (svga->interlace && svga->oddeven) ? 1 : 0; svga->scrollcache = (svga->attrregs[0x13] & 0x0f); if (!(svga->gdcreg[6] & 1) && !(svga->attrregs[0x10] & 1)) { /*Text mode*/ if (svga->seqregs[1] & 1) svga->scrollcache &= 0x07; else { svga->scrollcache++; if (svga->scrollcache > 8) svga->scrollcache = 0; } } else if ((svga->render == svga_render_2bpp_lowres) || (svga->render == svga_render_2bpp_highres) || (svga->render == svga_render_4bpp_lowres) || (svga->render == svga_render_4bpp_highres)) svga->scrollcache &= 0x07; else svga->scrollcache = (svga->scrollcache & 0x06) >> 1; if ((svga->seqregs[1] & 8) || (svga->render == svga_render_8bpp_lowres)) svga->scrollcache <<= 1; svga->x_add = (svga->monitor->mon_overscan_x >> 1) - svga->scrollcache; svga->linecountff = 0; svga->hwcursor_on = 0; svga->hwcursor_latch = svga->hwcursor; svga->dac_hwcursor_on = 0; svga->dac_hwcursor_latch = svga->dac_hwcursor; svga->overlay_on = 0; svga->overlay_latch = svga->overlay; } if (svga->sc == (svga->crtc[10] & 31)) svga->con = 1; } } uint32_t svga_conv_16to32(struct svga_t *svga, uint16_t color, uint8_t bpp) { return (bpp == 15) ? video_15to32[color] : video_16to32[color]; } int svga_init(const device_t *info, svga_t *svga, void *priv, int memsize, void (*recalctimings_ex)(struct svga_t *svga), uint8_t (*video_in)(uint16_t addr, void *priv), void (*video_out)(uint16_t addr, uint8_t val, void *priv), void (*hwcursor_draw)(struct svga_t *svga, int displine), void (*overlay_draw)(struct svga_t *svga, int displine)) { int e; svga->priv = priv; svga->monitor_index = monitor_index_global; svga->monitor = &monitors[svga->monitor_index]; for (int c = 0; c < 256; c++) { e = c; for (int d = 0; d < 8; d++) { svga_rotate[d][c] = e; e = (e >> 1) | ((e & 1) ? 0x80 : 0); } } svga->readmode = 0; svga->attrregs[0x11] = 0; svga->overscan_color = 0x000000; svga->monitor->mon_overscan_x = 16; svga->monitor->mon_overscan_y = 32; svga->x_add = 8; svga->y_add = 16; svga->crtc[0] = 63; svga->crtc[6] = 255; svga->dispontime = 1000ULL << 32; svga->dispofftime = 1000ULL << 32; svga->bpp = 8; svga->vram = calloc(memsize + 8, 1); svga->vram_max = memsize; svga->vram_display_mask = svga->vram_mask = memsize - 1; svga->decode_mask = 0x7fffff; svga->changedvram = calloc((memsize >> 12) + 1, 1); svga->recalctimings_ex = recalctimings_ex; svga->video_in = video_in; svga->video_out = video_out; svga->hwcursor_draw = hwcursor_draw; svga->overlay_draw = overlay_draw; svga->conv_16to32 = svga_conv_16to32; svga->render = svga_render_blank; svga->hwcursor.cur_xsize = svga->hwcursor.cur_ysize = 32; svga->dac_hwcursor.cur_xsize = svga->dac_hwcursor.cur_ysize = 32; svga->translate_address = NULL; svga->ksc5601_english_font_type = 0; if ((info->flags & DEVICE_PCI) || (info->flags & DEVICE_VLB) || (info->flags & DEVICE_MCA)) { mem_mapping_add(&svga->mapping, 0xa0000, 0x20000, svga_read, svga_readw, svga_readl, svga_write, svga_writew, svga_writel, NULL, MEM_MAPPING_EXTERNAL, svga); } else if ((info->flags & DEVICE_ISA) && (info->flags & DEVICE_AT)) { mem_mapping_add(&svga->mapping, 0xa0000, 0x20000, svga_read, svga_readw, NULL, svga_write, svga_writew, NULL, NULL, MEM_MAPPING_EXTERNAL, svga); } else { mem_mapping_add(&svga->mapping, 0xa0000, 0x20000, svga_read, NULL, NULL, svga_write, NULL, NULL, NULL, MEM_MAPPING_EXTERNAL, svga); } timer_add(&svga->timer, svga_poll, svga, 1); svga_pri = svga; svga->ramdac_type = RAMDAC_6BIT; svga->map8 = svga->pallook; return 0; } void svga_close(svga_t *svga) { free(svga->changedvram); free(svga->vram); if (svga->dpms_ui) ui_sb_set_text_w(NULL); svga_pri = NULL; } uint32_t svga_decode_addr(svga_t *svga, uint32_t addr, int write) { int memory_map_mode = (svga->gdcreg[6] >> 2) & 3; addr &= 0x1ffff; switch (memory_map_mode) { case 0: break; case 1: if (addr >= 0x10000) return 0xffffffff; break; case 2: addr -= 0x10000; if (addr >= 0x8000) return 0xffffffff; break; default: case 3: addr -= 0x18000; if (addr >= 0x8000) return 0xffffffff; break; } if (memory_map_mode <= 1) { if (write) addr += svga->write_bank; else addr += svga->read_bank; } return addr; } static __inline void svga_write_common(uint32_t addr, uint8_t val, uint8_t linear, void *priv) { svga_t *svga = (svga_t *) priv; int writemask2 = svga->writemask; int reset_wm = 0; latch_t vall; uint8_t wm = svga->writemask; uint8_t count; uint8_t i; if (svga->adv_flags & FLAG_ADDR_BY8) writemask2 = svga->seqregs[2]; cycles -= svga->monitor->mon_video_timing_write_b; if (!linear) { xga_write_test(addr, val, svga); addr = svga_decode_addr(svga, addr, 1); if (addr == 0xffffffff) { svga_log("WriteCommon Over.\n"); return; } } if (!(svga->gdcreg[6] & 1)) svga->fullchange = 2; if ((svga->adv_flags & FLAG_ADDR_BY16) && (svga->writemode == 4 || svga->writemode == 5)) addr <<= 4; else if ((svga->adv_flags & FLAG_ADDR_BY8) && (svga->writemode < 4)) addr <<= 3; else if (((svga->chain4 && (svga->packed_chain4 || svga->force_old_addr)) || svga->fb_only) && (svga->writemode < 4)) { writemask2 = 1 << (addr & 3); addr &= ~3; } else if (svga->chain4 && (svga->writemode < 4)) { writemask2 = 1 << (addr & 3); if (!linear) addr &= ~3; addr = ((addr & 0xfffc) << 2) | ((addr & 0x30000) >> 14) | (addr & ~0x3ffff); } else if (svga->chain2_write) { writemask2 &= ~0xa; if (addr & 1) writemask2 <<= 1; addr &= ~1; addr <<= 2; } else addr <<= 2; addr &= svga->decode_mask; if (svga->translate_address) addr = svga->translate_address(addr, priv); if (addr >= svga->vram_max) { svga_log("WriteBankedOver=%08x, val=%02x.\n", addr & svga->vram_mask, val); return; } addr &= svga->vram_mask; svga->changedvram[addr >> 12] = svga->monitor->mon_changeframecount; count = 4; if (svga->adv_flags & FLAG_LATCH8) count = 8; /* Undocumented Cirrus Logic behavior: The datasheet says that, with EXT_WRITE and FLAG_ADDR_BY8, the write mask only changes meaning in write modes 4 and 5, as well as write mode 1. In reality, however, all other write modes are also affected, as proven by the Windows 3.1 CL-GD 5422/4 drivers in 8bpp modes. */ switch (svga->writemode) { case 0: val = ((val >> (svga->gdcreg[3] & 7)) | (val << (8 - (svga->gdcreg[3] & 7)))); if ((svga->gdcreg[8] == 0xff) && !(svga->gdcreg[3] & 0x18) && (!svga->gdcreg[1] || svga->set_reset_disabled)) { for (i = 0; i < count; i++) { if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { if (writemask2 & (0x80 >> i)) svga->vram[addr | i] = val; } else { if (writemask2 & (1 << i)) svga->vram[addr | i] = val; } } return; } else { for (i = 0; i < count; i++) { if (svga->gdcreg[1] & (1 << i)) vall.b[i] = !!(svga->gdcreg[0] & (1 << i)) * 0xff; else vall.b[i] = val; } } break; case 1: for (i = 0; i < count; i++) { if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { if (writemask2 & (0x80 >> i)) svga->vram[addr | i] = svga->latch.b[i]; } else { if (writemask2 & (1 << i)) svga->vram[addr | i] = svga->latch.b[i]; } } return; case 2: for (i = 0; i < count; i++) vall.b[i] = !!(val & (1 << i)) * 0xff; if (!(svga->gdcreg[3] & 0x18) && (!svga->gdcreg[1] || svga->set_reset_disabled)) { for (i = 0; i < count; i++) { if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { if (writemask2 & (0x80 >> i)) svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | (svga->latch.b[i] & ~svga->gdcreg[8]); } else { if (writemask2 & (1 << i)) svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | (svga->latch.b[i] & ~svga->gdcreg[8]); } } return; } break; case 3: val = ((val >> (svga->gdcreg[3] & 7)) | (val << (8 - (svga->gdcreg[3] & 7)))); wm = svga->gdcreg[8]; svga->gdcreg[8] &= val; for (i = 0; i < count; i++) vall.b[i] = !!(svga->gdcreg[0] & (1 << i)) * 0xff; reset_wm = 1; break; default: if (svga->ven_write) svga->ven_write(svga, val, addr); return; } switch (svga->gdcreg[3] & 0x18) { case 0x00: /* Set */ for (i = 0; i < count; i++) { if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { if (writemask2 & (0x80 >> i)) svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | (svga->latch.b[i] & ~svga->gdcreg[8]); } else { if (writemask2 & (1 << i)) svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | (svga->latch.b[i] & ~svga->gdcreg[8]); } } break; case 0x08: /* AND */ for (i = 0; i < count; i++) { if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { if (writemask2 & (0x80 >> i)) svga->vram[addr | i] = (vall.b[i] | ~svga->gdcreg[8]) & svga->latch.b[i]; } else { if (writemask2 & (1 << i)) svga->vram[addr | i] = (vall.b[i] | ~svga->gdcreg[8]) & svga->latch.b[i]; } } break; case 0x10: /* OR */ for (i = 0; i < count; i++) { if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { if (writemask2 & (0x80 >> i)) svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | svga->latch.b[i]; } else { if (writemask2 & (1 << i)) svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | svga->latch.b[i]; } } break; case 0x18: /* XOR */ for (i = 0; i < count; i++) { if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { if (writemask2 & (0x80 >> i)) svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) ^ svga->latch.b[i]; } else { if (writemask2 & (1 << i)) svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) ^ svga->latch.b[i]; } } break; default: break; } if (reset_wm) svga->gdcreg[8] = wm; } static __inline uint8_t svga_read_common(uint32_t addr, uint8_t linear, void *priv) { svga_t *svga = (svga_t *) priv; uint32_t latch_addr = 0; int readplane = svga->readplane; uint8_t count; uint8_t temp; uint8_t ret = 0x00; if (svga->adv_flags & FLAG_ADDR_BY8) readplane = svga->gdcreg[4] & 7; cycles -= svga->monitor->mon_video_timing_read_b; if (!linear) { (void) xga_read_test(addr, svga); addr = svga_decode_addr(svga, addr, 0); if (addr == 0xffffffff) return 0xff; } count = 2; if (svga->adv_flags & FLAG_LATCH8) count = 3; latch_addr = (addr << count) & svga->decode_mask; count = (1 << count); if (svga->adv_flags & FLAG_ADDR_BY16) addr <<= 4; else if (svga->adv_flags & FLAG_ADDR_BY8) addr <<= 3; else if ((svga->chain4 && (svga->packed_chain4 || svga->force_old_addr)) || svga->fb_only) { addr &= svga->decode_mask; if (svga->translate_address) addr = svga->translate_address(addr, priv); if (addr >= svga->vram_max) return 0xff; latch_addr = (addr & svga->vram_mask) & ~3; for (uint8_t i = 0; i < count; i++) svga->latch.b[i] = svga->vram[latch_addr | i]; return svga->vram[addr & svga->vram_mask]; } else if (svga->chain4 && !svga->force_old_addr) { readplane = addr & 3; addr = ((addr & 0xfffc) << 2) | ((addr & 0x30000) >> 14) | (addr & ~0x3ffff); } else if (svga->chain2_read) { readplane = (readplane & 2) | (addr & 1); addr &= ~1; addr <<= 2; } else addr <<= 2; addr &= svga->decode_mask; if (svga->translate_address) { latch_addr = svga->translate_address(latch_addr, priv); addr = svga->translate_address(addr, priv); } /* standard VGA latched access */ if (latch_addr >= svga->vram_max) { for (uint8_t i = 0; i < count; i++) svga->latch.b[i] = 0xff; } else { latch_addr &= svga->vram_mask; for (uint8_t i = 0; i < count; i++) svga->latch.b[i] = svga->vram[latch_addr | i]; } if (addr >= svga->vram_max) return 0xff; addr &= svga->vram_mask; if (svga->readmode) { temp = 0xff; for (uint8_t pixel = 0; pixel < 8; pixel++) { for (uint8_t plane = 0; plane < count; plane++) { if (svga->colournocare & (1 << plane)) { /* If we care about a plane, and the pixel has a mismatch on it, clear its bit. */ if (((svga->latch.b[plane] >> pixel) & 1) != ((svga->colourcompare >> plane) & 1)) temp &= ~(1 << pixel); } } } ret = temp; } else ret = svga->vram[addr | readplane]; return ret; } void svga_write(uint32_t addr, uint8_t val, void *priv) { svga_write_common(addr, val, 0, priv); } void svga_write_linear(uint32_t addr, uint8_t val, void *priv) { svga_write_common(addr, val, 1, priv); } uint8_t svga_read(uint32_t addr, void *priv) { return svga_read_common(addr, 0, priv); } uint8_t svga_read_linear(uint32_t addr, void *priv) { return svga_read_common(addr, 1, priv); } void svga_doblit(int wx, int wy, svga_t *svga) { int y_add; int x_add; int y_start; int x_start; int bottom; uint32_t *p; int i; int j; int xs_temp; int ys_temp; y_add = enable_overscan ? svga->monitor->mon_overscan_y : 0; x_add = enable_overscan ? svga->monitor->mon_overscan_x : 0; y_start = enable_overscan ? 0 : (svga->monitor->mon_overscan_y >> 1); x_start = enable_overscan ? 0 : (svga->monitor->mon_overscan_x >> 1); bottom = (svga->monitor->mon_overscan_y >> 1); if (svga->vertical_linedbl) { y_add <<= 1; y_start <<= 1; bottom <<= 1; } if ((wx <= 0) || (wy <= 0)) return; if (svga->vertical_linedbl) svga->y_add <<= 1; xs_temp = wx; ys_temp = wy + 1; if (svga->vertical_linedbl) ys_temp++; if (xs_temp < 64) xs_temp = 640; if (ys_temp < 32) ys_temp = 200; if ((svga->crtc[0x17] & 0x80) && ((xs_temp != svga->monitor->mon_xsize) || (ys_temp != svga->monitor->mon_ysize) || video_force_resize_get_monitor(svga->monitor_index))) { /* Screen res has changed.. fix up, and let them know. */ svga->monitor->mon_xsize = xs_temp; svga->monitor->mon_ysize = ys_temp; if ((svga->monitor->mon_xsize > 1984) || (svga->monitor->mon_ysize > 2016)) { /* 2048x2048 is the biggest safe render texture, to account for overscan, we suppress overscan starting from x 1984 and y 2016. */ x_add = 0; y_add = 0; suppress_overscan = 1; } else suppress_overscan = 0; /* Block resolution changes while in DPMS mode to avoid getting a bogus screen width (320). We're already rendering a blank screen anyway. */ if (!svga->dpms) set_screen_size_monitor(svga->monitor->mon_xsize + x_add, svga->monitor->mon_ysize + y_add, svga->monitor_index); if (video_force_resize_get_monitor(svga->monitor_index)) video_force_resize_set_monitor(0, svga->monitor_index); } if ((wx >= 160) && ((wy + 1) >= 120)) { /* Draw (overscan_size - scroll size) lines of overscan on top and bottom. */ for (i = 0; i < svga->y_add; i++) { p = &svga->monitor->target_buffer->line[i & 0x7ff][0]; for (j = 0; j < (svga->monitor->mon_xsize + x_add); j++) p[j] = svga->dpms ? 0 : svga->overscan_color; } for (i = 0; i < bottom; i++) { p = &svga->monitor->target_buffer->line[(svga->monitor->mon_ysize + svga->y_add + i) & 0x7ff][0]; for (j = 0; j < (svga->monitor->mon_xsize + x_add); j++) p[j] = svga->dpms ? 0 : svga->overscan_color; } } video_blit_memtoscreen_monitor(x_start, y_start, svga->monitor->mon_xsize + x_add, svga->monitor->mon_ysize + y_add, svga->monitor_index); if (svga->vertical_linedbl) svga->vertical_linedbl >>= 1; } void svga_writeb_linear(uint32_t addr, uint8_t val, void *priv) { svga_t *svga = (svga_t *) priv; if (!svga->fast) { svga_write_linear(addr, val, priv); return; } addr &= svga->decode_mask; if (addr >= svga->vram_max) return; addr &= svga->vram_mask; svga->changedvram[addr >> 12] = svga->monitor->mon_changeframecount; svga->vram[addr] = val; } void svga_writew_common(uint32_t addr, uint16_t val, uint8_t linear, void *priv) { svga_t *svga = (svga_t *) priv; if (!svga->fast) { svga_write_common(addr, val, linear, priv); svga_write_common(addr + 1, val >> 8, linear, priv); return; } cycles -= svga->monitor->mon_video_timing_write_w; if (!linear) { xga_write_test(addr, val & 0xff, svga); xga_write_test(addr + 1, val >> 8, svga); addr = svga_decode_addr(svga, addr, 1); if (addr == 0xffffffff) return; } addr &= svga->decode_mask; if (svga->translate_address) { uint32_t addr2 = svga->translate_address(addr, priv); if (addr2 < svga->vram_max) { svga->vram[addr2 & svga->vram_mask] = val & 0xff; svga->changedvram[addr2 >> 12] = svga->monitor->mon_changeframecount; } addr2 = svga->translate_address(addr + 1, priv); if (addr2 < svga->vram_max) { svga->vram[addr2 & svga->vram_mask] = (val >> 8) & 0xff; svga->changedvram[addr2 >> 12] = svga->monitor->mon_changeframecount; } return; } if (addr >= svga->vram_max) return; addr &= svga->vram_mask; svga->changedvram[addr >> 12] = svga->monitor->mon_changeframecount; *(uint16_t *) &svga->vram[addr] = val; } void svga_writew(uint32_t addr, uint16_t val, void *priv) { svga_writew_common(addr, val, 0, priv); } void svga_writew_linear(uint32_t addr, uint16_t val, void *priv) { svga_writew_common(addr, val, 1, priv); } void svga_writel_common(uint32_t addr, uint32_t val, uint8_t linear, void *priv) { svga_t *svga = (svga_t *) priv; if (!svga->fast) { svga_write_common(addr, val, linear, priv); svga_write_common(addr + 1, val >> 8, linear, priv); svga_write_common(addr + 2, val >> 16, linear, priv); svga_write_common(addr + 3, val >> 24, linear, priv); return; } cycles -= svga->monitor->mon_video_timing_write_l; if (!linear) { xga_write_test(addr, val & 0xff, svga); xga_write_test(addr + 1, (val >> 8) & 0xff, svga); xga_write_test(addr + 2, (val >> 16) & 0xff, svga); xga_write_test(addr + 3, (val >> 24) & 0xff, svga); addr = svga_decode_addr(svga, addr, 1); if (addr == 0xffffffff) return; } addr &= svga->decode_mask; if (svga->translate_address) { uint32_t addr2 = svga->translate_address(addr, priv); if (addr2 < svga->vram_max) { svga->vram[addr2 & svga->vram_mask] = val & 0xff; svga->changedvram[addr2 >> 12] = svga->monitor->mon_changeframecount; } addr2 = svga->translate_address(addr + 1, priv); if (addr2 < svga->vram_max) { svga->vram[addr2 & svga->vram_mask] = (val >> 8) & 0xff; svga->changedvram[addr2 >> 12] = svga->monitor->mon_changeframecount; } addr2 = svga->translate_address(addr + 2, priv); if (addr2 < svga->vram_max) { svga->vram[addr2 & svga->vram_mask] = (val >> 16) & 0xff; svga->changedvram[addr2 >> 12] = svga->monitor->mon_changeframecount; } addr2 = svga->translate_address(addr + 3, priv); if (addr2 < svga->vram_max) { svga->vram[addr2 & svga->vram_mask] = (val >> 24) & 0xff; svga->changedvram[addr2 >> 12] = svga->monitor->mon_changeframecount; } return; } if (addr >= svga->vram_max) return; addr &= svga->vram_mask; svga->changedvram[addr >> 12] = svga->monitor->mon_changeframecount; *(uint32_t *) &svga->vram[addr] = val; } void svga_writel(uint32_t addr, uint32_t val, void *priv) { svga_writel_common(addr, val, 0, priv); } void svga_writel_linear(uint32_t addr, uint32_t val, void *priv) { svga_writel_common(addr, val, 1, priv); } uint8_t svga_readb_linear(uint32_t addr, void *priv) { const svga_t *svga = (svga_t *) priv; if (!svga->fast) return svga_read_linear(addr, priv); addr &= svga->decode_mask; if (addr >= svga->vram_max) return 0xff; return svga->vram[addr & svga->vram_mask]; } uint16_t svga_readw_common(uint32_t addr, uint8_t linear, void *priv) { svga_t *svga = (svga_t *) priv; if (!svga->fast) return svga_read_common(addr, linear, priv) | (svga_read_common(addr + 1, linear, priv) << 8); cycles -= svga->monitor->mon_video_timing_read_w; if (!linear) { addr = svga_decode_addr(svga, addr, 0); if (addr == 0xffffffff) return 0xffff; } addr &= svga->decode_mask; if (svga->translate_address) { uint8_t val1 = 0xff; uint8_t val2 = 0xff; uint32_t addr2 = svga->translate_address(addr, priv); if (addr2 < svga->vram_max) val1 = svga->vram[addr2 & svga->vram_mask]; addr2 = svga->translate_address(addr + 1, priv); if (addr2 < svga->vram_max) val2 = svga->vram[addr2 & svga->vram_mask]; return (val2 << 8) | val1; } if (addr >= svga->vram_max) return 0xffff; return *(uint16_t *) &svga->vram[addr & svga->vram_mask]; } uint16_t svga_readw(uint32_t addr, void *priv) { return svga_readw_common(addr, 0, priv); } uint16_t svga_readw_linear(uint32_t addr, void *priv) { return svga_readw_common(addr, 1, priv); } uint32_t svga_readl_common(uint32_t addr, uint8_t linear, void *priv) { svga_t *svga = (svga_t *) priv; if (!svga->fast) return svga_read_common(addr, linear, priv) | (svga_read_common(addr + 1, linear, priv) << 8) | (svga_read_common(addr + 2, linear, priv) << 16) | (svga_read_common(addr + 3, linear, priv) << 24); cycles -= svga->monitor->mon_video_timing_read_l; if (!linear) { addr = svga_decode_addr(svga, addr, 0); if (addr == 0xffffffff) return 0xffffffff; } addr &= svga->decode_mask; if (svga->translate_address) { uint8_t val1 = 0xff; uint8_t val2 = 0xff; uint8_t val3 = 0xff; uint8_t val4 = 0xff; uint32_t addr2 = svga->translate_address(addr, priv); if (addr2 < svga->vram_max) val1 = svga->vram[addr2 & svga->vram_mask]; addr2 = svga->translate_address(addr + 1, priv); if (addr2 < svga->vram_max) val2 = svga->vram[addr2 & svga->vram_mask]; addr2 = svga->translate_address(addr + 2, priv); if (addr2 < svga->vram_max) val3 = svga->vram[addr2 & svga->vram_mask]; addr2 = svga->translate_address(addr + 3, priv); if (addr2 < svga->vram_max) val4 = svga->vram[addr2 & svga->vram_mask]; return (val4 << 24) | (val3 << 16) | (val2 << 8) | val1; } if (addr >= svga->vram_max) return 0xffffffff; return *(uint32_t *) &svga->vram[addr & svga->vram_mask]; } uint32_t svga_readl(uint32_t addr, void *priv) { return svga_readl_common(addr, 0, priv); } uint32_t svga_readl_linear(uint32_t addr, void *priv) { return svga_readl_common(addr, 1, priv); }