1566 lines
67 KiB
C
1566 lines
67 KiB
C
/*S3 ViRGE emulation
|
|
|
|
The SVGA core is largely the same as older S3 chips, but the blitter is totally different*/
|
|
#include <stdlib.h>
|
|
#include "ibm.h"
|
|
#include "device.h"
|
|
#include "io.h"
|
|
#include "mem.h"
|
|
#include "pci.h"
|
|
#include "rom.h"
|
|
#include "video.h"
|
|
#include "vid_s3_virge.h"
|
|
#include "vid_svga.h"
|
|
#include "vid_svga_render.h"
|
|
//#include "vid_sdac_ramdac.h"
|
|
|
|
typedef struct virge_t
|
|
{
|
|
mem_mapping_t linear_mapping;
|
|
mem_mapping_t mmio_mapping;
|
|
mem_mapping_t new_mmio_mapping;
|
|
|
|
rom_t bios_rom;
|
|
|
|
svga_t svga;
|
|
|
|
uint8_t bank;
|
|
uint8_t ma_ext;
|
|
int width;
|
|
int bpp;
|
|
|
|
uint8_t virge_id, virge_id_high, virge_id_low, virge_rev;
|
|
|
|
uint32_t linear_base, linear_size;
|
|
|
|
uint8_t pci_regs[256];
|
|
|
|
struct
|
|
{
|
|
uint32_t src_base;
|
|
uint32_t dest_base;
|
|
int clip_l, clip_r, clip_t, clip_b;
|
|
int dest_str, src_str;
|
|
uint32_t mono_pat_0;
|
|
uint32_t mono_pat_1;
|
|
uint32_t pat_bg_clr;
|
|
uint32_t pat_fg_clr;
|
|
uint32_t src_bg_clr;
|
|
uint32_t src_fg_clr;
|
|
uint32_t cmd_set;
|
|
int r_width, r_height;
|
|
int rsrc_x, rsrc_y;
|
|
int rdest_x, rdest_y;
|
|
|
|
int lxend0, lxend1;
|
|
int32_t ldx;
|
|
uint32_t lxstart, lystart;
|
|
int lycnt;
|
|
int line_dir;
|
|
|
|
int src_x, src_y;
|
|
int dest_x, dest_y;
|
|
int w, h;
|
|
uint8_t rop;
|
|
|
|
int data_left_count;
|
|
uint32_t data_left;
|
|
|
|
uint32_t pattern_8[8*8];
|
|
uint32_t pattern_16[8*8];
|
|
uint32_t pattern_32[8*8];
|
|
} s3d;
|
|
|
|
struct
|
|
{
|
|
uint32_t pri_ctrl;
|
|
uint32_t chroma_ctrl;
|
|
uint32_t sec_ctrl;
|
|
uint32_t chroma_upper_bound;
|
|
uint32_t sec_filter;
|
|
uint32_t blend_ctrl;
|
|
uint32_t pri_fb0, pri_fb1;
|
|
uint32_t pri_stride;
|
|
uint32_t buffer_ctrl;
|
|
uint32_t sec_fb0, sec_fb1;
|
|
uint32_t sec_stride;
|
|
uint32_t overlay_ctrl;
|
|
uint32_t k1_vert_scale;
|
|
uint32_t k2_vert_scale;
|
|
uint32_t dda_vert_accumulator;
|
|
uint32_t fifo_ctrl;
|
|
uint32_t pri_start;
|
|
uint32_t pri_size;
|
|
uint32_t sec_start;
|
|
uint32_t sec_size;
|
|
|
|
int pri_x, pri_y, pri_w, pri_h;
|
|
int sec_x, sec_y, sec_w, sec_h;
|
|
} streams;
|
|
} virge_t;
|
|
|
|
static void s3_virge_recalctimings(svga_t *svga);
|
|
static void s3_virge_updatemapping(virge_t *virge);
|
|
|
|
static void s3_virge_bitblt(virge_t *virge, int count, uint32_t cpu_dat);
|
|
|
|
static uint8_t s3_virge_mmio_read(uint32_t addr, void *p);
|
|
static uint16_t s3_virge_mmio_read_w(uint32_t addr, void *p);
|
|
static uint32_t s3_virge_mmio_read_l(uint32_t addr, void *p);
|
|
static void s3_virge_mmio_write(uint32_t addr, uint8_t val, void *p);
|
|
static void s3_virge_mmio_write_w(uint32_t addr, uint16_t val, void *p);
|
|
static void s3_virge_mmio_write_l(uint32_t addr, uint32_t val, void *p);
|
|
|
|
enum
|
|
{
|
|
CMD_SET_AE = 1,
|
|
CMD_SET_HC = (1 << 1),
|
|
|
|
CMD_SET_FORMAT_MASK = (7 << 2),
|
|
CMD_SET_FORMAT_8 = (0 << 2),
|
|
CMD_SET_FORMAT_16 = (1 << 2),
|
|
CMD_SET_FORMAT_24 = (2 << 2),
|
|
|
|
CMD_SET_MS = (1 << 6),
|
|
CMD_SET_IDS = (1 << 7),
|
|
CMD_SET_MP = (1 << 8),
|
|
CMD_SET_TP = (1 << 9),
|
|
|
|
CMD_SET_ITA_MASK = (3 << 10),
|
|
CMD_SET_ITA_BYTE = (0 << 10),
|
|
CMD_SET_ITA_WORD = (1 << 10),
|
|
CMD_SET_ITA_DWORD = (2 << 10),
|
|
|
|
CMD_SET_XP = (1 << 25),
|
|
CMD_SET_YP = (1 << 26),
|
|
|
|
CMD_SET_COMMAND_MASK = (15 << 27)
|
|
};
|
|
|
|
enum
|
|
{
|
|
CMD_SET_COMMAND_BITBLT = (0 << 27),
|
|
CMD_SET_COMMAND_RECTFILL = (2 << 27),
|
|
CMD_SET_COMMAND_LINE = (3 << 27),
|
|
CMD_SET_COMMAND_NOP = (15 << 27)
|
|
};
|
|
|
|
static void s3_virge_out(uint16_t addr, uint8_t val, void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
svga_t *svga = &virge->svga;
|
|
uint8_t old;
|
|
|
|
if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1))
|
|
addr ^= 0x60;
|
|
|
|
// pclog("S3 out %04X %02X %04X:%08X %04X %04X %i\n", addr, val, CS, pc, ES, BX, ins);
|
|
|
|
switch (addr)
|
|
{
|
|
case 0x3c5:
|
|
if (svga->seqaddr >= 0x10)
|
|
{
|
|
svga->seqregs[svga->seqaddr & 0x1f]=val;
|
|
s3_virge_recalctimings(svga);
|
|
return;
|
|
}
|
|
if (svga->seqaddr == 4) /*Chain-4 - update banking*/
|
|
{
|
|
if (val & 8) svga->write_bank = svga->read_bank = virge->bank << 16;
|
|
else svga->write_bank = svga->read_bank = virge->bank << 14;
|
|
}
|
|
break;
|
|
|
|
//case 0x3C6: case 0x3C7: case 0x3C8: case 0x3C9:
|
|
// pclog("Write RAMDAC %04X %02X %04X:%04X\n", addr, val, CS, pc);
|
|
//sdac_ramdac_out(addr,val);
|
|
//return;
|
|
|
|
case 0x3d4:
|
|
svga->crtcreg = val & 0x7f;
|
|
return;
|
|
case 0x3d5:
|
|
// pclog("Write CRTC R%02X %02X\n", svga->crtcreg, val);
|
|
if (svga->crtcreg <= 7 && svga->crtc[0x11] & 0x80)
|
|
return;
|
|
if (svga->crtcreg >= 0x20 && svga->crtcreg != 0x38 && (svga->crtc[0x38] & 0xcc) != 0x48)
|
|
return;
|
|
old = svga->crtc[svga->crtcreg];
|
|
svga->crtc[svga->crtcreg] = val;
|
|
switch (svga->crtcreg)
|
|
{
|
|
case 0x31:
|
|
virge->ma_ext = (virge->ma_ext & 0x1c) | ((val & 0x30) >> 4);
|
|
svga->vrammask = (val & 8) ? 0x3fffff : 0x3ffff;
|
|
break;
|
|
|
|
case 0x50:
|
|
switch (svga->crtc[0x50] & 0xc1)
|
|
{
|
|
case 0x00: virge->width = (svga->crtc[0x31] & 2) ? 2048 : 1024; break;
|
|
case 0x01: virge->width = 1152; break;
|
|
case 0x40: virge->width = 640; break;
|
|
case 0x80: virge->width = 800; break;
|
|
case 0x81: virge->width = 1600; break;
|
|
case 0xc0: virge->width = 1280; break;
|
|
}
|
|
virge->bpp = (svga->crtc[0x50] >> 4) & 3;
|
|
break;
|
|
case 0x69:
|
|
virge->ma_ext = val & 0x1f;
|
|
break;
|
|
|
|
case 0x35:
|
|
virge->bank = (virge->bank & 0x70) | (val & 0xf);
|
|
// pclog("CRTC write R35 %02X\n", val);
|
|
if (svga->chain4) svga->write_bank = svga->read_bank = virge->bank << 16;
|
|
else svga->write_bank = svga->read_bank = virge->bank << 14;
|
|
break;
|
|
case 0x51:
|
|
virge->bank = (virge->bank & 0x4f) | ((val & 0xc) << 2);
|
|
if (svga->chain4) svga->write_bank = svga->read_bank = virge->bank << 16;
|
|
else svga->write_bank = svga->read_bank = virge->bank << 14;
|
|
virge->ma_ext = (virge->ma_ext & ~0xc) | ((val & 3) << 2);
|
|
break;
|
|
case 0x6a:
|
|
virge->bank = val;
|
|
// pclog("CRTC write R6a %02X\n", val);
|
|
if (svga->chain4) svga->write_bank = svga->read_bank = virge->bank << 16;
|
|
else svga->write_bank = svga->read_bank = virge->bank << 14;
|
|
break;
|
|
|
|
case 0x3a:
|
|
if (val & 0x10) svga->gdcreg[5] |= 0x40; /*Horrible cheat*/
|
|
break;
|
|
|
|
case 0x45:
|
|
svga->hwcursor.ena = val & 1;
|
|
break;
|
|
case 0x46: case 0x47: case 0x48: case 0x49:
|
|
case 0x4c: case 0x4d: case 0x4e: case 0x4f:
|
|
svga->hwcursor.x = ((svga->crtc[0x46] << 8) | svga->crtc[0x47]) & 0x7ff;
|
|
svga->hwcursor.y = ((svga->crtc[0x48] << 8) | svga->crtc[0x49]) & 0x7ff;
|
|
svga->hwcursor.xoff = svga->crtc[0x4e] & 63;
|
|
svga->hwcursor.yoff = svga->crtc[0x4f] & 63;
|
|
svga->hwcursor.addr = ((((svga->crtc[0x4c] << 8) | svga->crtc[0x4d]) & 0xfff) * 1024) + (svga->hwcursor.yoff * 16);
|
|
break;
|
|
|
|
case 0x53:
|
|
case 0x58: case 0x59: case 0x5a:
|
|
s3_virge_updatemapping(virge);
|
|
break;
|
|
|
|
case 0x67:
|
|
switch (val >> 4)
|
|
{
|
|
case 3: svga->bpp = 15; break;
|
|
case 5: svga->bpp = 16; break;
|
|
case 7: svga->bpp = 24; break;
|
|
case 13: svga->bpp = 32; break;
|
|
default: svga->bpp = 8; break;
|
|
}
|
|
break;
|
|
//case 0x55: case 0x43:
|
|
// pclog("Write CRTC R%02X %02X\n", crtcreg, val);
|
|
}
|
|
if (old != val)
|
|
{
|
|
if (svga->crtcreg < 0xe || svga->crtcreg > 0x10)
|
|
{
|
|
svga->fullchange = changeframecount;
|
|
svga_recalctimings(svga);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
svga_out(addr, val, svga);
|
|
}
|
|
|
|
static uint8_t s3_virge_in(uint16_t addr, void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
svga_t *svga = &virge->svga;
|
|
|
|
if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1))
|
|
addr ^= 0x60;
|
|
|
|
// if (addr != 0x3da) pclog("S3 in %04X %04X:%08X\n", addr, CS, pc);
|
|
switch (addr)
|
|
{
|
|
//case 0x3C6: case 0x3C7: case 0x3C8: case 0x3C9:
|
|
// pclog("Read RAMDAC %04X %04X:%04X\n", addr, CS, pc);
|
|
//return sdac_ramdac_in(addr);
|
|
|
|
case 0x3c5:
|
|
if (svga->seqaddr >= 0x10)
|
|
return svga->seqregs[svga->seqaddr & 0x1f];
|
|
break;
|
|
|
|
case 0x3D4:
|
|
return svga->crtcreg;
|
|
case 0x3D5:
|
|
// pclog("Read CRTC R%02X %04X:%04X (%02x)\n", svga->crtcreg, CS, pc, svga->crtc[svga->crtcreg]);
|
|
switch (svga->crtcreg)
|
|
{
|
|
case 0x2d: return virge->virge_id_high; /*Extended chip ID*/
|
|
case 0x2e: return virge->virge_id_low; /*New chip ID*/
|
|
case 0x2f: return virge->virge_rev;
|
|
case 0x30: return virge->virge_id; /*Chip ID*/
|
|
case 0x31: return (svga->crtc[0x31] & 0xcf) | ((virge->ma_ext & 3) << 4);
|
|
case 0x35: return (svga->crtc[0x35] & 0xf0) | (virge->bank & 0xf);
|
|
case 0x36: return (svga->crtc[0x36] & 0xfc) | 2; /*PCI bus*/
|
|
case 0x51: return (svga->crtc[0x51] & 0xf0) | ((virge->bank >> 2) & 0xc) | ((virge->ma_ext >> 2) & 3);
|
|
case 0x69: return virge->ma_ext;
|
|
case 0x6a: return virge->bank;
|
|
}
|
|
return svga->crtc[svga->crtcreg];
|
|
}
|
|
return svga_in(addr, svga);
|
|
}
|
|
|
|
static void s3_virge_recalctimings(svga_t *svga)
|
|
{
|
|
virge_t *virge = (virge_t *)svga->p;
|
|
|
|
if (svga->crtc[0x5d] & 0x01) svga->htotal += 0x100;
|
|
if (svga->crtc[0x5d] & 0x02) svga->hdisp += 0x100;
|
|
if (svga->crtc[0x5e] & 0x01) svga->vtotal += 0x400;
|
|
if (svga->crtc[0x5e] & 0x02) svga->dispend += 0x400;
|
|
if (svga->crtc[0x5e] & 0x04) svga->vblankstart += 0x400;
|
|
if (svga->crtc[0x5e] & 0x10) svga->vsyncstart += 0x400;
|
|
if (svga->crtc[0x5e] & 0x40) svga->split += 0x400;
|
|
svga->interlace = svga->crtc[0x42] & 0x20;
|
|
|
|
if ((svga->crtc[0x67] & 0xc) != 0xc) /*VGA mode*/
|
|
{
|
|
svga->ma_latch |= (virge->ma_ext << 16);
|
|
|
|
if (svga->crtc[0x51] & 0x30) svga->rowoffset += (svga->crtc[0x51] & 0x30) << 4;
|
|
else if (svga->crtc[0x43] & 0x04) svga->rowoffset += 0x100;
|
|
if (!svga->rowoffset) svga->rowoffset = 256;
|
|
|
|
if ((svga->gdcreg[5] & 0x40) && (svga->crtc[0x3a] & 0x10))
|
|
{
|
|
switch (svga->bpp)
|
|
{
|
|
case 8:
|
|
svga->render = svga_render_8bpp_highres;
|
|
break;
|
|
case 15:
|
|
svga->render = svga_render_15bpp_highres;
|
|
break;
|
|
case 16:
|
|
svga->render = svga_render_16bpp_highres;
|
|
break;
|
|
case 24:
|
|
svga->render = svga_render_24bpp_highres;
|
|
break;
|
|
case 32:
|
|
svga->render = svga_render_32bpp_highres;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// pclog("svga->rowoffset = %i bpp=%i\n", svga->rowoffset, svga->bpp);
|
|
if (svga->bpp == 15 || svga->bpp == 16)
|
|
{
|
|
svga->htotal >>= 1;
|
|
svga->hdisp >>= 1;
|
|
}
|
|
if (svga->bpp == 24)
|
|
{
|
|
svga->rowoffset = (svga->rowoffset * 3) / 4; /*Hack*/
|
|
}
|
|
}
|
|
else /*Streams mode*/
|
|
{
|
|
if (virge->streams.buffer_ctrl & 1)
|
|
svga->ma_latch = virge->streams.pri_fb1 >> 2;
|
|
else
|
|
svga->ma_latch = virge->streams.pri_fb0 >> 2;
|
|
|
|
svga->rowoffset = virge->streams.pri_stride >> 3;
|
|
|
|
switch ((virge->streams.pri_ctrl >> 24) & 0x7)
|
|
{
|
|
case 0: /*RGB-8 (CLUT)*/
|
|
svga->render = svga_render_8bpp_highres;
|
|
break;
|
|
case 3: /*KRGB-16 (1.5.5.5)*/
|
|
svga->render = svga_render_15bpp_highres;
|
|
break;
|
|
case 5: /*RGB-16 (5.6.5)*/
|
|
svga->render = svga_render_16bpp_highres;
|
|
break;
|
|
case 6: /*RGB-24 (8.8.8)*/
|
|
svga->render = svga_render_24bpp_highres;
|
|
break;
|
|
case 7: /*XRGB-32 (X.8.8.8)*/
|
|
svga->render = svga_render_32bpp_highres;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (((svga->miscout >> 2) & 3) == 3)
|
|
{
|
|
int n = svga->seqregs[0x12] & 0x1f;
|
|
int r = (svga->seqregs[0x12] >> 5) & 3;
|
|
int m = svga->seqregs[0x13] & 0x7f;
|
|
double freq = (((double)m + 2) / (((double)n + 2) * (double)(1 << r))) * 14318184.0;
|
|
|
|
svga->clock = cpuclock / freq;
|
|
}
|
|
}
|
|
|
|
static void s3_virge_updatemapping(virge_t *virge)
|
|
{
|
|
svga_t *svga = &virge->svga;
|
|
|
|
if (!(virge->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_MEM))
|
|
{
|
|
// pclog("Update mapping - PCI disabled\n");
|
|
mem_mapping_disable(&svga->mapping);
|
|
mem_mapping_disable(&virge->linear_mapping);
|
|
mem_mapping_disable(&virge->mmio_mapping);
|
|
mem_mapping_disable(&virge->new_mmio_mapping);
|
|
return;
|
|
}
|
|
|
|
pclog("Update mapping - bank %02X ", svga->gdcreg[6] & 0xc);
|
|
switch (svga->gdcreg[6] & 0xc) /*Banked framebuffer*/
|
|
{
|
|
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;
|
|
}
|
|
|
|
virge->linear_base = (svga->crtc[0x5a] << 16) | (svga->crtc[0x59] << 24);
|
|
|
|
pclog("Linear framebuffer %02X ", svga->crtc[0x58] & 0x10);
|
|
if (svga->crtc[0x58] & 0x10) /*Linear framebuffer*/
|
|
{
|
|
switch (svga->crtc[0x58] & 3)
|
|
{
|
|
case 0: /*64k*/
|
|
virge->linear_size = 0x10000;
|
|
break;
|
|
case 1: /*1mb*/
|
|
virge->linear_size = 0x100000;
|
|
break;
|
|
case 2: /*2mb*/
|
|
virge->linear_size = 0x200000;
|
|
break;
|
|
case 3: /*8mb*/
|
|
virge->linear_size = 0x400000;
|
|
break;
|
|
}
|
|
virge->linear_base &= ~(virge->linear_size - 1);
|
|
// pclog("%08X %08X %02X %02X %02X\n", linear_base, linear_size, crtc[0x58], crtc[0x59], crtc[0x5a]);
|
|
pclog("Linear framebuffer at %08X size %08X\n", virge->linear_base, virge->linear_size);
|
|
if (virge->linear_base == 0xa0000)
|
|
{
|
|
mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x10000);
|
|
mem_mapping_disable(&virge->linear_mapping);
|
|
}
|
|
else
|
|
mem_mapping_set_addr(&virge->linear_mapping, virge->linear_base, virge->linear_size);
|
|
}
|
|
else
|
|
mem_mapping_disable(&virge->linear_mapping);
|
|
|
|
pclog("Memory mapped IO %02X\n", svga->crtc[0x53] & 0x18);
|
|
if (svga->crtc[0x53] & 0x10) /*Old MMIO*/
|
|
{
|
|
if (svga->crtc[0x53] & 0x20)
|
|
mem_mapping_set_addr(&virge->mmio_mapping, 0xb8000, 0x8000);
|
|
else
|
|
mem_mapping_set_addr(&virge->mmio_mapping, 0xa0000, 0x10000);
|
|
}
|
|
else
|
|
mem_mapping_disable(&virge->mmio_mapping);
|
|
|
|
if (svga->crtc[0x53] & 0x08) /*New MMIO*/
|
|
mem_mapping_set_addr(&virge->new_mmio_mapping, virge->linear_base + 0x1000000, 0x10000);
|
|
else
|
|
mem_mapping_disable(&virge->new_mmio_mapping);
|
|
|
|
}
|
|
|
|
|
|
static uint8_t s3_virge_mmio_read(uint32_t addr, void *p)
|
|
{
|
|
// pclog("New MMIO readb %08X\n", addr);
|
|
switch (addr & 0xffff)
|
|
{
|
|
case 0x83b0: case 0x83b1: case 0x83b2: case 0x83b3:
|
|
case 0x83b4: case 0x83b5: case 0x83b6: case 0x83b7:
|
|
case 0x83b8: case 0x83b9: case 0x83ba: case 0x83bb:
|
|
case 0x83bc: case 0x83bd: case 0x83be: case 0x83bf:
|
|
case 0x83c0: case 0x83c1: case 0x83c2: case 0x83c3:
|
|
case 0x83c4: case 0x83c5: case 0x83c6: case 0x83c7:
|
|
case 0x83c8: case 0x83c9: case 0x83ca: case 0x83cb:
|
|
case 0x83cc: case 0x83cd: case 0x83ce: case 0x83cf:
|
|
case 0x83d0: case 0x83d1: case 0x83d2: case 0x83d3:
|
|
case 0x83d4: case 0x83d5: case 0x83d6: case 0x83d7:
|
|
case 0x83d8: case 0x83d9: case 0x83da: case 0x83db:
|
|
case 0x83dc: case 0x83dd: case 0x83de: case 0x83df:
|
|
return s3_virge_in(addr & 0x3ff, p);
|
|
}
|
|
return 0xff;
|
|
}
|
|
static uint16_t s3_virge_mmio_read_w(uint32_t addr, void *p)
|
|
{
|
|
// pclog("New MMIO readw %08X\n", addr);
|
|
switch (addr & 0xfffe)
|
|
{
|
|
default:
|
|
return s3_virge_mmio_read(addr, p) | (s3_virge_mmio_read(addr + 1, p) << 8);
|
|
}
|
|
return 0xffff;
|
|
}
|
|
static uint32_t s3_virge_mmio_read_l(uint32_t addr, void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
uint32_t ret = 0xffffffff;
|
|
// pclog("New MMIO readl %08X\n", addr);
|
|
switch (addr & 0xfffc)
|
|
{
|
|
case 0x8180:
|
|
ret = virge->streams.pri_ctrl;
|
|
break;
|
|
case 0x8184:
|
|
ret = virge->streams.chroma_ctrl;
|
|
break;
|
|
case 0x8190:
|
|
ret = virge->streams.sec_ctrl;
|
|
break;
|
|
case 0x8194:
|
|
ret = virge->streams.chroma_upper_bound;
|
|
break;
|
|
case 0x8198:
|
|
ret = virge->streams.sec_filter;
|
|
break;
|
|
case 0x81a0:
|
|
ret = virge->streams.blend_ctrl;
|
|
break;
|
|
case 0x81c0:
|
|
ret = virge->streams.pri_fb0;
|
|
break;
|
|
case 0x81c4:
|
|
ret = virge->streams.pri_fb1;
|
|
break;
|
|
case 0x81c8:
|
|
ret = virge->streams.pri_stride;
|
|
break;
|
|
case 0x81cc:
|
|
ret = virge->streams.buffer_ctrl;
|
|
break;
|
|
case 0x81d0:
|
|
ret = virge->streams.sec_fb0;
|
|
break;
|
|
case 0x81d4:
|
|
ret = virge->streams.sec_fb1;
|
|
break;
|
|
case 0x81d8:
|
|
ret = virge->streams.sec_stride;
|
|
break;
|
|
case 0x81dc:
|
|
ret = virge->streams.overlay_ctrl;
|
|
break;
|
|
case 0x81e0:
|
|
ret = virge->streams.k1_vert_scale;
|
|
break;
|
|
case 0x81e4:
|
|
ret = virge->streams.k2_vert_scale;
|
|
break;
|
|
case 0x81e8:
|
|
ret = virge->streams.dda_vert_accumulator;
|
|
break;
|
|
case 0x81ec:
|
|
ret = virge->streams.fifo_ctrl;
|
|
break;
|
|
case 0x81f0:
|
|
ret = virge->streams.pri_start;
|
|
break;
|
|
case 0x81f4:
|
|
ret = virge->streams.pri_size;
|
|
break;
|
|
case 0x81f8:
|
|
ret = virge->streams.sec_start;
|
|
break;
|
|
case 0x81fc:
|
|
ret = virge->streams.sec_size;
|
|
break;
|
|
|
|
case 0x8504:
|
|
ret = (0x1f << 8) | (1 << 13);
|
|
break;
|
|
case 0xa4d4:
|
|
ret = virge->s3d.src_base;
|
|
break;
|
|
case 0xa4d8:
|
|
ret = virge->s3d.dest_base;
|
|
break;
|
|
case 0xa4dc:
|
|
ret = (virge->s3d.clip_l << 16) | virge->s3d.clip_r;
|
|
break;
|
|
case 0xa4e0:
|
|
ret = (virge->s3d.clip_t << 16) | virge->s3d.clip_b;
|
|
break;
|
|
case 0xa4e4:
|
|
ret = (virge->s3d.dest_str << 16) | virge->s3d.src_str;
|
|
break;
|
|
case 0xa4e8:
|
|
ret = virge->s3d.mono_pat_0;
|
|
break;
|
|
case 0xa4ec:
|
|
ret = virge->s3d.mono_pat_1;
|
|
break;
|
|
case 0xa4f0:
|
|
ret = virge->s3d.pat_bg_clr;
|
|
break;
|
|
case 0xa4f4:
|
|
ret = virge->s3d.pat_fg_clr;
|
|
break;
|
|
case 0xa4f8:
|
|
ret = virge->s3d.src_bg_clr;
|
|
break;
|
|
case 0xa4fc:
|
|
ret = virge->s3d.src_fg_clr;
|
|
break;
|
|
case 0xa500:
|
|
ret = virge->s3d.cmd_set;
|
|
break;
|
|
case 0xa504:
|
|
ret = (virge->s3d.r_width << 16) | virge->s3d.r_height;
|
|
break;
|
|
case 0xa508:
|
|
ret = (virge->s3d.rsrc_x << 16) | virge->s3d.rsrc_y;
|
|
break;
|
|
case 0xa50c:
|
|
ret = (virge->s3d.rdest_x << 16) | virge->s3d.rdest_y;
|
|
break;
|
|
|
|
default:
|
|
return s3_virge_mmio_read_w(addr, p) | (s3_virge_mmio_read_w(addr + 2, p) << 16);
|
|
}
|
|
return ret;
|
|
}
|
|
static void s3_virge_mmio_write(uint32_t addr, uint8_t val, void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
svga_t *svga = &virge->svga;
|
|
|
|
// pclog("New MMIO writeb %08X %02X\n", addr, val);
|
|
|
|
if ((addr & 0xfffc) < 0x8000)
|
|
s3_virge_bitblt(virge, 8, val);
|
|
else switch (addr & 0xffff)
|
|
{
|
|
case 0x83b0: case 0x83b1: case 0x83b2: case 0x83b3:
|
|
case 0x83b4: case 0x83b5: case 0x83b6: case 0x83b7:
|
|
case 0x83b8: case 0x83b9: case 0x83ba: case 0x83bb:
|
|
case 0x83bc: case 0x83bd: case 0x83be: case 0x83bf:
|
|
case 0x83c0: case 0x83c1: case 0x83c2: case 0x83c3:
|
|
case 0x83c4: case 0x83c5: case 0x83c6: case 0x83c7:
|
|
case 0x83c8: case 0x83c9: case 0x83ca: case 0x83cb:
|
|
case 0x83cc: case 0x83cd: case 0x83ce: case 0x83cf:
|
|
case 0x83d0: case 0x83d1: case 0x83d2: case 0x83d3:
|
|
case 0x83d4: case 0x83d5: case 0x83d6: case 0x83d7:
|
|
case 0x83d8: case 0x83d9: case 0x83da: case 0x83db:
|
|
case 0x83dc: case 0x83dd: case 0x83de: case 0x83df:
|
|
s3_virge_out(addr & 0x3ff, val, p);
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
static void s3_virge_mmio_write_w(uint32_t addr, uint16_t val, void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
// pclog("New MMIO writew %08X %04X\n", addr, val);
|
|
if ((addr & 0xfffc) < 0x8000)
|
|
{
|
|
if (virge->s3d.cmd_set & CMD_SET_MS)
|
|
s3_virge_bitblt(virge, 16, ((val >> 8) | (val << 8)) << 16);
|
|
else
|
|
s3_virge_bitblt(virge, 16, val);
|
|
}
|
|
else switch (addr & 0xfffe)
|
|
{
|
|
case 0x83d4:
|
|
s3_virge_mmio_write(addr, val, p);
|
|
s3_virge_mmio_write(addr + 1, val >> 8, p);
|
|
break;
|
|
}
|
|
}
|
|
static void s3_virge_mmio_write_l(uint32_t addr, uint32_t val, void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
svga_t *svga = &virge->svga;
|
|
// if ((addr & 0xfffc) >= 0x8000)
|
|
// pclog("New MMIO writel %08X %08X\n", addr, val);
|
|
|
|
if ((addr & 0xfffc) < 0x8000)
|
|
{
|
|
if (virge->s3d.cmd_set & CMD_SET_MS)
|
|
s3_virge_bitblt(virge, 32, ((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24));
|
|
else
|
|
s3_virge_bitblt(virge, 32, val);
|
|
}
|
|
else switch (addr & 0xfffc)
|
|
{
|
|
case 0x8180:
|
|
virge->streams.pri_ctrl = val;
|
|
s3_virge_recalctimings(svga);
|
|
svga->fullchange = changeframecount;
|
|
break;
|
|
case 0x8184:
|
|
virge->streams.chroma_ctrl = val;
|
|
break;
|
|
case 0x8190:
|
|
virge->streams.sec_ctrl = val;
|
|
break;
|
|
case 0x8194:
|
|
virge->streams.chroma_upper_bound = val;
|
|
break;
|
|
case 0x8198:
|
|
virge->streams.sec_filter = val;
|
|
break;
|
|
case 0x81a0:
|
|
virge->streams.blend_ctrl = val;
|
|
break;
|
|
case 0x81c0:
|
|
virge->streams.pri_fb0 = val & 0x3fffff;
|
|
s3_virge_recalctimings(svga);
|
|
svga->fullchange = changeframecount;
|
|
break;
|
|
case 0x81c4:
|
|
virge->streams.pri_fb1 = val & 0x3fffff;
|
|
s3_virge_recalctimings(svga);
|
|
svga->fullchange = changeframecount;
|
|
break;
|
|
case 0x81c8:
|
|
virge->streams.pri_stride = val & 0xfff;
|
|
s3_virge_recalctimings(svga);
|
|
svga->fullchange = changeframecount;
|
|
break;
|
|
case 0x81cc:
|
|
virge->streams.buffer_ctrl = val;
|
|
s3_virge_recalctimings(svga);
|
|
break;
|
|
case 0x81d0:
|
|
virge->streams.sec_fb0 = val;
|
|
break;
|
|
case 0x81d4:
|
|
virge->streams.sec_fb1 = val;
|
|
break;
|
|
case 0x81d8:
|
|
virge->streams.sec_stride = val;
|
|
break;
|
|
case 0x81dc:
|
|
virge->streams.overlay_ctrl = val;
|
|
break;
|
|
case 0x81e0:
|
|
virge->streams.k1_vert_scale = val;
|
|
break;
|
|
case 0x81e4:
|
|
virge->streams.k2_vert_scale = val;
|
|
break;
|
|
case 0x81e8:
|
|
virge->streams.dda_vert_accumulator = val;
|
|
break;
|
|
case 0x81ec:
|
|
virge->streams.fifo_ctrl = val;
|
|
break;
|
|
case 0x81f0:
|
|
virge->streams.pri_start = val;
|
|
virge->streams.pri_x = (val >> 16) & 0x7ff;
|
|
virge->streams.pri_y = val & 0x7ff;
|
|
s3_virge_recalctimings(svga);
|
|
svga->fullchange = changeframecount;
|
|
break;
|
|
case 0x81f4:
|
|
virge->streams.pri_size = val;
|
|
virge->streams.pri_w = (val >> 16) & 0x7ff;
|
|
virge->streams.pri_h = val & 0x7ff;
|
|
s3_virge_recalctimings(svga);
|
|
svga->fullchange = changeframecount;
|
|
break;
|
|
case 0x81f8:
|
|
virge->streams.sec_start = val;
|
|
virge->streams.sec_x = (val >> 16) & 0x7ff;
|
|
virge->streams.sec_y = val & 0x7ff;
|
|
break;
|
|
case 0x81fc:
|
|
virge->streams.sec_size = val;
|
|
virge->streams.sec_w = (val >> 16) & 0x7ff;
|
|
virge->streams.sec_h = val & 0x7ff;
|
|
break;
|
|
|
|
case 0xa000: case 0xa004: case 0xa008: case 0xa00c:
|
|
case 0xa010: case 0xa014: case 0xa018: case 0xa01c:
|
|
case 0xa020: case 0xa024: case 0xa028: case 0xa02c:
|
|
case 0xa030: case 0xa034: case 0xa038: case 0xa03c:
|
|
case 0xa040: case 0xa044: case 0xa048: case 0xa04c:
|
|
case 0xa050: case 0xa054: case 0xa058: case 0xa05c:
|
|
case 0xa060: case 0xa064: case 0xa068: case 0xa06c:
|
|
case 0xa070: case 0xa074: case 0xa078: case 0xa07c:
|
|
case 0xa080: case 0xa084: case 0xa088: case 0xa08c:
|
|
case 0xa090: case 0xa094: case 0xa098: case 0xa09c:
|
|
case 0xa0a0: case 0xa0a4: case 0xa0a8: case 0xa0ac:
|
|
case 0xa0b0: case 0xa0b4: case 0xa0b8: case 0xa0bc:
|
|
case 0xa0c0: case 0xa0c4: case 0xa0c8: case 0xa0cc:
|
|
case 0xa0d0: case 0xa0d4: case 0xa0d8: case 0xa0dc:
|
|
case 0xa0e0: case 0xa0e4: case 0xa0e8: case 0xa0ec:
|
|
case 0xa0f0: case 0xa0f4: case 0xa0f8: case 0xa0fc:
|
|
case 0xa100: case 0xa104: case 0xa108: case 0xa10c:
|
|
case 0xa110: case 0xa114: case 0xa118: case 0xa11c:
|
|
case 0xa120: case 0xa124: case 0xa128: case 0xa12c:
|
|
case 0xa130: case 0xa134: case 0xa138: case 0xa13c:
|
|
case 0xa140: case 0xa144: case 0xa148: case 0xa14c:
|
|
case 0xa150: case 0xa154: case 0xa158: case 0xa15c:
|
|
case 0xa160: case 0xa164: case 0xa168: case 0xa16c:
|
|
case 0xa170: case 0xa174: case 0xa178: case 0xa17c:
|
|
case 0xa180: case 0xa184: case 0xa188: case 0xa18c:
|
|
case 0xa190: case 0xa194: case 0xa198: case 0xa19c:
|
|
case 0xa1a0: case 0xa1a4: case 0xa1a8: case 0xa1ac:
|
|
case 0xa1b0: case 0xa1b4: case 0xa1b8: case 0xa1bc:
|
|
case 0xa1c0: case 0xa1c4: case 0xa1c8: case 0xa1cc:
|
|
case 0xa1d0: case 0xa1d4: case 0xa1d8: case 0xa1dc:
|
|
case 0xa1e0: case 0xa1e4: case 0xa1e8: case 0xa1ec:
|
|
case 0xa1f0: case 0xa1f4: case 0xa1f8: case 0xa1fc:
|
|
{
|
|
int x = addr & 4;
|
|
int y = (addr >> 3) & 7;
|
|
virge->s3d.pattern_8[y*8 + x] = val & 0xff;
|
|
virge->s3d.pattern_8[y*8 + x + 1] = val >> 8;
|
|
virge->s3d.pattern_8[y*8 + x + 2] = val >> 16;
|
|
virge->s3d.pattern_8[y*8 + x + 3] = val >> 24;
|
|
|
|
x = (addr >> 1) & 6;
|
|
y = (addr >> 4) & 7;
|
|
virge->s3d.pattern_16[y*8 + x] = val & 0xffff;
|
|
virge->s3d.pattern_16[y*8 + x + 1] = val >> 16;
|
|
|
|
x = (addr >> 2) & 7;
|
|
y = (addr >> 5) & 7;
|
|
virge->s3d.pattern_32[y*8 + x] = val & 0xffffff;
|
|
}
|
|
break;
|
|
|
|
|
|
case 0xa4d4: case 0xa8d4:
|
|
virge->s3d.src_base = val & 0x3ffff8;
|
|
break;
|
|
case 0xa4d8: case 0xa8d8:
|
|
virge->s3d.dest_base = val & 0x3ffff8;
|
|
break;
|
|
case 0xa4dc: case 0xa8dc:
|
|
virge->s3d.clip_l = (val >> 16) & 0x7ff;
|
|
virge->s3d.clip_r = val & 0x7ff;
|
|
break;
|
|
case 0xa4e0: case 0xa8e0:
|
|
virge->s3d.clip_t = (val >> 16) & 0x7ff;
|
|
virge->s3d.clip_b = val & 0x7ff;
|
|
break;
|
|
case 0xa4e4: case 0xa8e4:
|
|
virge->s3d.dest_str = (val >> 16) & 0xff8;
|
|
virge->s3d.src_str = val & 0xff8;
|
|
break;
|
|
case 0xa4e8:
|
|
virge->s3d.mono_pat_0 = val;
|
|
break;
|
|
case 0xa4ec:
|
|
virge->s3d.mono_pat_1 = val;
|
|
break;
|
|
case 0xa4f0:
|
|
virge->s3d.pat_bg_clr = val;
|
|
break;
|
|
case 0xa4f4: case 0xa8f4:
|
|
virge->s3d.pat_fg_clr = val;
|
|
break;
|
|
case 0xa4f8:
|
|
virge->s3d.src_bg_clr = val;
|
|
break;
|
|
case 0xa4fc:
|
|
virge->s3d.src_fg_clr = val;
|
|
break;
|
|
case 0xa500: case 0xa900:
|
|
virge->s3d.cmd_set = val;
|
|
if (!(val & CMD_SET_AE))
|
|
s3_virge_bitblt(virge, -1, 0);
|
|
break;
|
|
case 0xa504:
|
|
virge->s3d.r_width = (val >> 16) & 0x7ff;
|
|
virge->s3d.r_height = val & 0x7ff;
|
|
break;
|
|
case 0xa508:
|
|
virge->s3d.rsrc_x = (val >> 16) & 0x7ff;
|
|
virge->s3d.rsrc_y = val & 0x7ff;
|
|
break;
|
|
case 0xa50c:
|
|
virge->s3d.rdest_x = (val >> 16) & 0x7ff;
|
|
virge->s3d.rdest_y = val & 0x7ff;
|
|
if (virge->s3d.cmd_set & CMD_SET_AE)
|
|
s3_virge_bitblt(virge, -1, 0);
|
|
break;
|
|
case 0xa96c:
|
|
virge->s3d.lxend0 = (val >> 16) & 0x7ff;
|
|
virge->s3d.lxend1 = val & 0x7ff;
|
|
break;
|
|
case 0xa970:
|
|
virge->s3d.ldx = (int32_t)val;
|
|
break;
|
|
case 0xa974:
|
|
virge->s3d.lxstart = val;
|
|
break;
|
|
case 0xa978:
|
|
virge->s3d.lystart = val & 0x7ff;
|
|
break;
|
|
case 0xa97c:
|
|
virge->s3d.lycnt = val & 0x7ff;
|
|
virge->s3d.line_dir = val >> 31;
|
|
if (virge->s3d.cmd_set & CMD_SET_AE)
|
|
s3_virge_bitblt(virge, -1, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define READ(addr, val) \
|
|
do \
|
|
{ \
|
|
switch (bpp) \
|
|
{ \
|
|
case 0: /*8 bpp*/ \
|
|
val = vram[addr & 0x3fffff]; \
|
|
break; \
|
|
case 1: /*16 bpp*/ \
|
|
val = *(uint16_t *)&vram[addr & 0x3fffff]; \
|
|
break; \
|
|
case 2: /*24 bpp*/ \
|
|
val = (*(uint32_t *)&vram[addr & 0x3fffff]) & 0xffffff; \
|
|
break; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define CLIP(x, y) \
|
|
do \
|
|
{ \
|
|
if ((virge->s3d.cmd_set & CMD_SET_HC) && \
|
|
(x < virge->s3d.clip_l || \
|
|
x > virge->s3d.clip_r || \
|
|
y < virge->s3d.clip_t || \
|
|
y > virge->s3d.clip_b)) \
|
|
update = 0; \
|
|
} while (0)
|
|
|
|
#define MIX() \
|
|
do \
|
|
{ \
|
|
int c; \
|
|
for (c = 0; c < 24; c++) \
|
|
{ \
|
|
int d = (dest & (1 << c)) ? 1 : 0; \
|
|
if (source & (1 << c)) d |= 2; \
|
|
if (pattern & (1 << c)) d |= 4; \
|
|
if (virge->s3d.rop & (1 << d)) out |= (1 << c); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define WRITE(addr, val) \
|
|
do \
|
|
{ \
|
|
switch (bpp) \
|
|
{ \
|
|
case 0: /*8 bpp*/ \
|
|
vram[addr & 0x3fffff] = val; \
|
|
virge->svga.changedvram[(addr & 0x3fffff) >> 12] = changeframecount; \
|
|
break; \
|
|
case 1: /*16 bpp*/ \
|
|
*(uint16_t *)&vram[addr & 0x3fffff] = val; \
|
|
virge->svga.changedvram[(addr & 0x3fffff) >> 12] = changeframecount; \
|
|
break; \
|
|
case 2: /*24 bpp*/ \
|
|
*(uint32_t *)&vram[addr & 0x3fffff] = (val & 0xffffff) | \
|
|
(vram[(addr + 3) & 0x3fffff] << 24); \
|
|
virge->svga.changedvram[(addr & 0x3fffff) >> 12] = changeframecount; \
|
|
break; \
|
|
} \
|
|
} while (0)
|
|
|
|
static void s3_virge_bitblt(virge_t *virge, int count, uint32_t cpu_dat)
|
|
{
|
|
int cpu_input = (count != -1);
|
|
uint8_t *vram = virge->svga.vram;
|
|
uint32_t mono_pattern[64];
|
|
int count_mask;
|
|
int x_inc = (virge->s3d.cmd_set & CMD_SET_XP) ? 1 : -1;
|
|
int y_inc = (virge->s3d.cmd_set & CMD_SET_YP) ? 1 : -1;
|
|
int bpp;
|
|
int x_mul;
|
|
int cpu_dat_shift;
|
|
uint32_t *pattern_data;
|
|
|
|
switch (virge->s3d.cmd_set & CMD_SET_FORMAT_MASK)
|
|
{
|
|
case CMD_SET_FORMAT_8:
|
|
bpp = 0;
|
|
x_mul = 1;
|
|
cpu_dat_shift = 8;
|
|
pattern_data = virge->s3d.pattern_8;
|
|
break;
|
|
case CMD_SET_FORMAT_16:
|
|
bpp = 1;
|
|
x_mul = 2;
|
|
cpu_dat_shift = 16;
|
|
pattern_data = virge->s3d.pattern_16;
|
|
break;
|
|
case CMD_SET_FORMAT_24:
|
|
default:
|
|
bpp = 2;
|
|
x_mul = 3;
|
|
cpu_dat_shift = 24;
|
|
pattern_data = virge->s3d.pattern_32;
|
|
break;
|
|
}
|
|
if (virge->s3d.cmd_set & CMD_SET_MP)
|
|
pattern_data = mono_pattern;
|
|
|
|
switch (virge->s3d.cmd_set & CMD_SET_ITA_MASK)
|
|
{
|
|
case CMD_SET_ITA_BYTE:
|
|
count_mask = ~0x7;
|
|
break;
|
|
case CMD_SET_ITA_WORD:
|
|
count_mask = ~0xf;
|
|
break;
|
|
case CMD_SET_ITA_DWORD:
|
|
default:
|
|
count_mask = ~0x1f;
|
|
break;
|
|
}
|
|
if (virge->s3d.cmd_set & CMD_SET_MP)
|
|
{
|
|
int x, y;
|
|
for (y = 0; y < 4; y++)
|
|
{
|
|
for (x = 0; x < 8; x++)
|
|
{
|
|
if (virge->s3d.mono_pat_0 & (1 << (x + y*8)))
|
|
mono_pattern[y*8 + x] = virge->s3d.pat_fg_clr;
|
|
else
|
|
mono_pattern[y*8 + x] = virge->s3d.pat_bg_clr;
|
|
if (virge->s3d.mono_pat_1 & (1 << (x + y*8)))
|
|
mono_pattern[(y+4)*8 + x] = virge->s3d.pat_fg_clr;
|
|
else
|
|
mono_pattern[(y+4)*8 + x] = virge->s3d.pat_bg_clr;
|
|
}
|
|
}
|
|
}
|
|
switch (virge->s3d.cmd_set & CMD_SET_COMMAND_MASK)
|
|
{
|
|
case CMD_SET_COMMAND_NOP:
|
|
break;
|
|
|
|
case CMD_SET_COMMAND_BITBLT:
|
|
if (count == -1)
|
|
{
|
|
virge->s3d.src_x = virge->s3d.rsrc_x;
|
|
virge->s3d.src_y = virge->s3d.rsrc_y;
|
|
virge->s3d.dest_x = virge->s3d.rdest_x;
|
|
virge->s3d.dest_y = virge->s3d.rdest_y;
|
|
virge->s3d.w = virge->s3d.r_width;
|
|
virge->s3d.h = virge->s3d.r_height;
|
|
virge->s3d.rop = (virge->s3d.cmd_set >> 17) & 0xff;
|
|
virge->s3d.data_left_count = 0;
|
|
|
|
/* pclog("BitBlt start %i,%i %i,%i %02X\n", virge->s3d.dest_x,
|
|
virge->s3d.dest_y,
|
|
virge->s3d.w,
|
|
virge->s3d.h,
|
|
virge->s3d.rop);*/
|
|
|
|
if (virge->s3d.cmd_set & CMD_SET_IDS)
|
|
return;
|
|
}
|
|
if (!virge->s3d.h)
|
|
return;
|
|
while (count)
|
|
{
|
|
uint32_t src_addr = virge->s3d.src_base + (virge->s3d.src_x * x_mul) + (virge->s3d.src_y * virge->s3d.src_str);
|
|
uint32_t dest_addr = virge->s3d.dest_base + (virge->s3d.dest_x * x_mul) + (virge->s3d.dest_y * virge->s3d.dest_str);
|
|
uint32_t source, dest, pattern;
|
|
uint32_t out = 0;
|
|
int update = 1;
|
|
|
|
switch (virge->s3d.cmd_set & (CMD_SET_MS | CMD_SET_IDS))
|
|
{
|
|
case 0:
|
|
case CMD_SET_MS:
|
|
READ(src_addr, source);
|
|
if ((virge->s3d.cmd_set & CMD_SET_TP) && source == virge->s3d.src_fg_clr)
|
|
update = 0;
|
|
break;
|
|
case CMD_SET_IDS:
|
|
if (virge->s3d.data_left_count)
|
|
{
|
|
/*Handle shifting for 24-bit data*/
|
|
source = virge->s3d.data_left;
|
|
source |= ((cpu_dat << virge->s3d.data_left_count) & ~0xff000000);
|
|
cpu_dat >>= (cpu_dat_shift - virge->s3d.data_left_count);
|
|
count -= (cpu_dat_shift - virge->s3d.data_left_count);
|
|
virge->s3d.data_left_count = 0;
|
|
if (count < cpu_dat_shift)
|
|
{
|
|
virge->s3d.data_left = cpu_dat;
|
|
virge->s3d.data_left_count = count;
|
|
count = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
source = cpu_dat;
|
|
cpu_dat >>= cpu_dat_shift;
|
|
count -= cpu_dat_shift;
|
|
if (count < cpu_dat_shift)
|
|
{
|
|
virge->s3d.data_left = cpu_dat;
|
|
virge->s3d.data_left_count = count;
|
|
count = 0;
|
|
}
|
|
}
|
|
if ((virge->s3d.cmd_set & CMD_SET_TP) && source == virge->s3d.src_fg_clr)
|
|
update = 0;
|
|
break;
|
|
case CMD_SET_IDS | CMD_SET_MS:
|
|
source = (cpu_dat & (1 << 31)) ? virge->s3d.src_fg_clr : virge->s3d.src_bg_clr;
|
|
if ((virge->s3d.cmd_set & CMD_SET_TP) && !(cpu_dat & (1 << 31)))
|
|
update = 0;
|
|
cpu_dat <<= 1;
|
|
count--;
|
|
break;
|
|
}
|
|
|
|
CLIP(virge->s3d.dest_x, virge->s3d.dest_y);
|
|
|
|
if (update)
|
|
{
|
|
READ(dest_addr, dest);
|
|
pattern = pattern_data[(virge->s3d.dest_y & 7)*8 + (virge->s3d.dest_x & 7)];
|
|
MIX();
|
|
|
|
WRITE(dest_addr, out);
|
|
}
|
|
|
|
virge->s3d.src_x += x_inc;
|
|
virge->s3d.dest_x += x_inc;
|
|
if (!virge->s3d.w)
|
|
{
|
|
virge->s3d.src_x = virge->s3d.rsrc_x;
|
|
virge->s3d.dest_x = virge->s3d.rdest_x;
|
|
virge->s3d.w = virge->s3d.r_width;
|
|
|
|
virge->s3d.src_y += y_inc;
|
|
virge->s3d.dest_y += y_inc;
|
|
virge->s3d.h--;
|
|
|
|
switch (virge->s3d.cmd_set & (CMD_SET_MS | CMD_SET_IDS))
|
|
{
|
|
case CMD_SET_IDS:
|
|
cpu_dat >>= (count - (count & count_mask));
|
|
count &= count_mask;
|
|
virge->s3d.data_left_count = 0;
|
|
break;
|
|
|
|
case CMD_SET_IDS | CMD_SET_MS:
|
|
cpu_dat <<= (count - (count & count_mask));
|
|
count &= count_mask;
|
|
break;
|
|
}
|
|
if (!virge->s3d.h)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
virge->s3d.w--;
|
|
}
|
|
break;
|
|
|
|
case CMD_SET_COMMAND_RECTFILL:
|
|
/*No source, pattern = pat_fg_clr*/
|
|
if (count == -1)
|
|
{
|
|
virge->s3d.src_x = virge->s3d.rsrc_x;
|
|
virge->s3d.src_y = virge->s3d.rsrc_y;
|
|
virge->s3d.dest_x = virge->s3d.rdest_x;
|
|
virge->s3d.dest_y = virge->s3d.rdest_y;
|
|
virge->s3d.w = virge->s3d.r_width;
|
|
virge->s3d.h = virge->s3d.r_height;
|
|
virge->s3d.rop = (virge->s3d.cmd_set >> 17) & 0xff;
|
|
|
|
/* pclog("RctFll start %i,%i %i,%i %02X\n", virge->s3d.dest_x,
|
|
virge->s3d.dest_y,
|
|
virge->s3d.w,
|
|
virge->s3d.h,
|
|
virge->s3d.rop);*/
|
|
}
|
|
|
|
while (count)
|
|
{
|
|
uint32_t dest_addr = virge->s3d.dest_base + (virge->s3d.dest_x * x_mul) + (virge->s3d.dest_y * virge->s3d.dest_str);
|
|
uint32_t source = 0, dest, pattern = virge->s3d.pat_fg_clr;
|
|
uint32_t out = 0;
|
|
int update = 1;
|
|
|
|
CLIP(virge->s3d.dest_x, virge->s3d.dest_y);
|
|
|
|
if (update)
|
|
{
|
|
READ(dest_addr, dest);
|
|
|
|
MIX();
|
|
|
|
WRITE(dest_addr, out);
|
|
}
|
|
|
|
virge->s3d.src_x += x_inc;
|
|
virge->s3d.dest_x += x_inc;
|
|
if (!virge->s3d.w)
|
|
{
|
|
virge->s3d.src_x = virge->s3d.rsrc_x;
|
|
virge->s3d.dest_x = virge->s3d.rdest_x;
|
|
virge->s3d.w = virge->s3d.r_width;
|
|
|
|
virge->s3d.src_y += y_inc;
|
|
virge->s3d.dest_y += y_inc;
|
|
virge->s3d.h--;
|
|
if (!virge->s3d.h)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
virge->s3d.w--;
|
|
count--;
|
|
}
|
|
break;
|
|
|
|
case CMD_SET_COMMAND_LINE:
|
|
if (count == -1)
|
|
{
|
|
virge->s3d.dest_x = virge->s3d.lxstart;
|
|
virge->s3d.dest_y = virge->s3d.lystart;
|
|
virge->s3d.h = virge->s3d.lycnt;
|
|
virge->s3d.rop = (virge->s3d.cmd_set >> 17) & 0xff;
|
|
if (virge->s3d.ldx >= 0)
|
|
virge->s3d.dest_x -= virge->s3d.ldx / 2;
|
|
else
|
|
virge->s3d.dest_x += virge->s3d.ldx / 2;
|
|
//virge->s3d.dest_dest_x = virge->s3d.dest_x + virge->s3d.ldx;
|
|
}
|
|
while (virge->s3d.h)
|
|
{
|
|
int x = virge->s3d.dest_x >> 20;
|
|
int new_x = (virge->s3d.dest_x + virge->s3d.ldx) >> 20;
|
|
|
|
do
|
|
{
|
|
uint32_t dest_addr = virge->s3d.dest_base + (x * x_mul) + (virge->s3d.dest_y * virge->s3d.dest_str);
|
|
uint32_t source = 0, dest, pattern;
|
|
uint32_t out = 0;
|
|
int update = 1;
|
|
|
|
CLIP(x, virge->s3d.dest_y);
|
|
|
|
if (update)
|
|
{
|
|
READ(dest_addr, dest);
|
|
pattern = virge->s3d.pat_fg_clr;
|
|
|
|
MIX();
|
|
|
|
WRITE(dest_addr, out);
|
|
}
|
|
|
|
if (x < new_x)
|
|
x++;
|
|
else if (x > new_x)
|
|
x--;
|
|
} while (x != new_x);
|
|
|
|
virge->s3d.dest_x += virge->s3d.ldx;
|
|
virge->s3d.dest_y--;
|
|
virge->s3d.h--;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fatal("s3_virge_bitblt : blit command %i %08x\n", (virge->s3d.cmd_set >> 27) & 0xf, virge->s3d.cmd_set);
|
|
}
|
|
}
|
|
|
|
|
|
static void s3_virge_hwcursor_draw(svga_t *svga, int displine)
|
|
{
|
|
int x;
|
|
uint16_t dat[2];
|
|
int xx;
|
|
int offset = svga->hwcursor_latch.x - svga->hwcursor_latch.xoff;
|
|
|
|
// pclog("HWcursor %i %i\n", svga->hwcursor_latch.x, svga->hwcursor_latch.y);
|
|
for (x = 0; x < 64; x += 16)
|
|
{
|
|
dat[0] = (svga->vram[svga->hwcursor_latch.addr] << 8) | svga->vram[svga->hwcursor_latch.addr + 1];
|
|
dat[1] = (svga->vram[svga->hwcursor_latch.addr + 2] << 8) | svga->vram[svga->hwcursor_latch.addr + 3];
|
|
for (xx = 0; xx < 16; xx++)
|
|
{
|
|
if (offset >= svga->hwcursor_latch.x)
|
|
{
|
|
if (!(dat[0] & 0x8000))
|
|
((uint32_t *)buffer32->line[displine])[offset + 32] = (dat[1] & 0x8000) ? 0xffffff : 0;
|
|
else if (dat[1] & 0x8000)
|
|
((uint32_t *)buffer32->line[displine])[offset + 32] ^= 0xffffff;
|
|
// pclog("Plot %i, %i (%i %i) %04X %04X\n", offset, displine, x+xx, svga->hwcursor_on, dat[0], dat[1]);
|
|
}
|
|
|
|
offset++;
|
|
dat[0] <<= 1;
|
|
dat[1] <<= 1;
|
|
}
|
|
svga->hwcursor_latch.addr += 4;
|
|
}
|
|
}
|
|
|
|
static uint8_t s3_virge_pci_read(int func, int addr, void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
svga_t *svga = &virge->svga;
|
|
uint8_t ret = 0;
|
|
// pclog("S3 PCI read %08X ", addr);
|
|
switch (addr)
|
|
{
|
|
case 0x00: ret = 0x33; break; /*'S3'*/
|
|
case 0x01: ret = 0x53; break;
|
|
|
|
case 0x02: ret = virge->virge_id_low; break;
|
|
case 0x03: ret = virge->virge_id_high; break;
|
|
|
|
case 0x04: ret = virge->pci_regs[0x04] & 0x27; break;
|
|
|
|
case 0x07: ret = virge->pci_regs[0x07] & 0x36; break;
|
|
|
|
case 0x08: ret = 0; break; /*Revision ID*/
|
|
case 0x09: ret = 0; break; /*Programming interface*/
|
|
|
|
case 0x0a: ret = 0x00; break; /*Supports VGA interface*/
|
|
case 0x0b: ret = 0x03; /*output = 3; */break;
|
|
|
|
case 0x0d: ret = virge->pci_regs[0x0d] & 0xf8; break;
|
|
|
|
case 0x10: ret = 0x00; break;/*Linear frame buffer address*/
|
|
case 0x11: ret = 0x00; break;
|
|
case 0x12: ret = 0x00; break;
|
|
case 0x13: ret = svga->crtc[0x59] & 0xfc; break;
|
|
|
|
case 0x30: ret = virge->pci_regs[0x30] & 0x01; break; /*BIOS ROM address*/
|
|
case 0x31: ret = 0x00; break;
|
|
case 0x32: ret = virge->pci_regs[0x32]; break;
|
|
case 0x33: ret = virge->pci_regs[0x33]; break;
|
|
|
|
case 0x3c: ret = virge->pci_regs[0x3c]; break;
|
|
|
|
case 0x3d: ret = 0x01; break; /*INTA*/
|
|
|
|
case 0x3e: ret = 0x04; break;
|
|
case 0x3f: ret = 0xff; break;
|
|
|
|
}
|
|
// pclog("%02X\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static void s3_virge_pci_write(int func, int addr, uint8_t val, void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
svga_t *svga = &virge->svga;
|
|
// pclog("S3 PCI write %08X %02X %04X:%08X\n", addr, val, CS, pc);
|
|
switch (addr)
|
|
{
|
|
case 0x00: case 0x01: case 0x02: case 0x03:
|
|
case 0x08: case 0x09: case 0x0a: case 0x0b:
|
|
case 0x3d: case 0x3e: case 0x3f:
|
|
return;
|
|
|
|
case PCI_REG_COMMAND:
|
|
if (val & PCI_COMMAND_IO)
|
|
{
|
|
io_removehandler(0x03c0, 0x0020, s3_virge_in, NULL, NULL, s3_virge_out, NULL, NULL, virge);
|
|
io_sethandler(0x03c0, 0x0020, s3_virge_in, NULL, NULL, s3_virge_out, NULL, NULL, virge);
|
|
}
|
|
else
|
|
io_removehandler(0x03c0, 0x0020, s3_virge_in, NULL, NULL, s3_virge_out, NULL, NULL, virge);
|
|
virge->pci_regs[PCI_REG_COMMAND] = val & 0x27;
|
|
return;
|
|
case 0x07:
|
|
virge->pci_regs[0x07] = val & 0x3e;
|
|
return;
|
|
case 0x0d:
|
|
virge->pci_regs[0x0d] = val & 0xf8;
|
|
return;
|
|
|
|
case 0x13:
|
|
svga->crtc[0x59] = val & 0xfc;
|
|
s3_virge_updatemapping(virge);
|
|
return;
|
|
|
|
case 0x30: case 0x32: case 0x33:
|
|
virge->pci_regs[addr] = val;
|
|
if (virge->pci_regs[0x30] & 0x01)
|
|
{
|
|
uint32_t addr = (virge->pci_regs[0x32] << 16) | (virge->pci_regs[0x33] << 24);
|
|
pclog("Virge bios_rom enabled at %08x\n", addr);
|
|
mem_mapping_set_addr(&virge->bios_rom.mapping, addr, 0x8000);
|
|
mem_mapping_enable(&virge->bios_rom.mapping);
|
|
}
|
|
else
|
|
{
|
|
pclog("Virge bios_rom disabled\n");
|
|
mem_mapping_disable(&virge->bios_rom.mapping);
|
|
}
|
|
return;
|
|
case 0x3c:
|
|
virge->pci_regs[0x3c] = val;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void *s3_virge_init()
|
|
{
|
|
virge_t *virge = malloc(sizeof(virge_t));
|
|
memset(virge, 0, sizeof(virge_t));
|
|
|
|
svga_init(&virge->svga, virge, 1 << 22, /*4mb*/
|
|
s3_virge_recalctimings,
|
|
s3_virge_in, s3_virge_out,
|
|
s3_virge_hwcursor_draw);
|
|
|
|
rom_init(&virge->bios_rom, "roms/s3virge.bin", 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL);
|
|
if (PCI)
|
|
mem_mapping_disable(&virge->bios_rom.mapping);
|
|
|
|
mem_mapping_add(&virge->mmio_mapping, 0, 0, s3_virge_mmio_read,
|
|
s3_virge_mmio_read_w,
|
|
s3_virge_mmio_read_l,
|
|
s3_virge_mmio_write,
|
|
s3_virge_mmio_write_w,
|
|
s3_virge_mmio_write_l,
|
|
NULL,
|
|
0,
|
|
virge);
|
|
mem_mapping_add(&virge->new_mmio_mapping, 0, 0, s3_virge_mmio_read,
|
|
s3_virge_mmio_read_w,
|
|
s3_virge_mmio_read_l,
|
|
s3_virge_mmio_write,
|
|
s3_virge_mmio_write_w,
|
|
s3_virge_mmio_write_l,
|
|
NULL,
|
|
0,
|
|
virge);
|
|
mem_mapping_add(&virge->linear_mapping, 0, 0, svga_read_linear,
|
|
svga_readw_linear,
|
|
svga_readl_linear,
|
|
svga_write_linear,
|
|
svga_writew_linear,
|
|
svga_writel_linear,
|
|
NULL,
|
|
0,
|
|
&virge->svga);
|
|
|
|
io_sethandler(0x03c0, 0x0020, s3_virge_in, NULL, NULL, s3_virge_out, NULL, NULL, virge);
|
|
|
|
virge->pci_regs[4] = 3;
|
|
virge->pci_regs[5] = 0;
|
|
virge->pci_regs[6] = 0;
|
|
virge->pci_regs[7] = 2;
|
|
virge->pci_regs[0x32] = 0x0c;
|
|
virge->pci_regs[0x3d] = 1;
|
|
virge->pci_regs[0x3e] = 4;
|
|
virge->pci_regs[0x3f] = 0xff;
|
|
|
|
virge->virge_id_high = 0x56;
|
|
virge->virge_id_low = 0x31;
|
|
virge->virge_rev = 0;
|
|
virge->virge_id = 0xe1;
|
|
|
|
virge->svga.crtc[0x36] = 2 | (0 << 2) | (1 << 4);
|
|
virge->svga.crtc[0x37] = 1;// | (7 << 5);
|
|
virge->svga.crtc[0x53] = 1 << 3;
|
|
virge->svga.crtc[0x59] = 0x70;
|
|
|
|
pci_add(s3_virge_pci_read, s3_virge_pci_write, virge);
|
|
|
|
return virge;
|
|
}
|
|
|
|
static void s3_virge_close(void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
|
|
svga_close(&virge->svga);
|
|
|
|
free(virge);
|
|
}
|
|
|
|
static int s3_virge_available()
|
|
{
|
|
return rom_present("roms/s3virge.bin");
|
|
}
|
|
|
|
static void s3_virge_speed_changed(void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
|
|
svga_recalctimings(&virge->svga);
|
|
}
|
|
|
|
static void s3_virge_force_redraw(void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
|
|
virge->svga.fullchange = changeframecount;
|
|
}
|
|
|
|
static int s3_virge_add_status_info(char *s, int max_len, void *p)
|
|
{
|
|
virge_t *virge = (virge_t *)p;
|
|
|
|
return svga_add_status_info(s, max_len, &virge->svga);
|
|
}
|
|
|
|
device_t s3_virge_device =
|
|
{
|
|
"Diamond Stealth 3D 2000 (S3 VIRGE)",
|
|
DEVICE_NOT_WORKING,
|
|
s3_virge_init,
|
|
s3_virge_close,
|
|
s3_virge_available,
|
|
s3_virge_speed_changed,
|
|
s3_virge_force_redraw,
|
|
s3_virge_add_status_info
|
|
};
|