pcem/src/vid_voodoo.c
2013-10-19 17:06:55 +01:00

743 lines
26 KiB
C

#include <stdlib.h>
#include "ibm.h"
#include "mem.h"
#include "pci.h"
#include "vid_voodoo.h"
static int tris = 0;
static struct voodoo
{
mem_mapping_t mmio_mapping;
mem_mapping_t fb_mapping;
mem_mapping_t tex_mapping;
int pci_enable;
uint32_t color0, color1;
uint8_t dac_data[8];
int dac_reg;
uint8_t dac_readdata;
uint32_t dRdX, dGdX, dBdX, dZdX, dAdX, dSdX, dTdX, dWdX;
uint32_t dRdY, dGdY, dBdY, dZdY, dAdY, dSdY, dTdY, dWdY;
uint32_t fbiInit0, fbiInit1, fbiInit2, fbiInit3, fbiInit4;
uint32_t fbzMode;
uint8_t initEnable;
uint32_t lfbMode;
uint32_t memBaseAddr;
uint32_t startR, startG, startB, startZ, startA, startS, startT, startW;
uint32_t vertexAx, vertexAy, vertexBx, vertexBy, vertexCx, vertexCy;
uint32_t front_offset, back_offset, aux_offset;
uint32_t fb_read_offset, fb_write_offset;
uint32_t draw_offset;
int row_width;
uint8_t *fb_mem, *tex_mem;
} voodoo;
enum
{
SST_status = 0x000,
SST_vertexAx = 0x008,
SST_vertexAy = 0x00c,
SST_vertexBx = 0x010,
SST_vertexBy = 0x014,
SST_vertexCx = 0x018,
SST_vertexCy = 0x01c,
SST_startR = 0x0020,
SST_startG = 0x0024,
SST_startB = 0x0028,
SST_startZ = 0x002c,
SST_startA = 0x0030,
SST_startS = 0x0034,
SST_startT = 0x0038,
SST_startW = 0x003c,
SST_dRdX = 0x0040,
SST_dGdX = 0x0044,
SST_dBdX = 0x0048,
SST_dZdX = 0x004c,
SST_dAdX = 0x0050,
SST_dSdX = 0x0054,
SST_dTdX = 0x0058,
SST_dWdX = 0x005c,
SST_dRdY = 0x0060,
SST_dGdY = 0x0064,
SST_dBdY = 0x0068,
SST_dZdY = 0x006c,
SST_dAdY = 0x0070,
SST_dSdY = 0x0074,
SST_dTdY = 0x0078,
SST_dWdY = 0x007c,
SST_triangleCMD = 0x0080,
SST_fbzMode = 0x110,
SST_lfbMode = 0x114,
SST_swapbufferCMD = 0x128,
SST_color0 = 0x144,
SST_color1 = 0x148,
SST_fbiInit4 = 0x200,
SST_fbiInit0 = 0x210,
SST_fbiInit1 = 0x214,
SST_fbiInit2 = 0x218,
SST_fbiInit3 = 0x21c,
SST_dacData = 0x22c
};
enum
{
LFB_WRITE_FRONT = 0x0000,
LFB_WRITE_BACK = 0x0010,
LFB_WRITE_MASK = 0x0030
};
enum
{
LFB_READ_FRONT = 0x0000,
LFB_READ_BACK = 0x0040,
LFB_READ_AUX = 0x0080,
LFB_READ_MASK = 0x00c0
};
enum
{
LFB_FORMAT_RGB565 = 0,
LFB_FORMAT_DEPTH = 15,
LFB_FORMAT_MASK = 15
};
enum
{
LFB_WRITE_COLOUR = 1,
LFB_WRITE_DEPTH = 2
};
enum
{
FBZ_DITHER = 0x100,
FBZ_DRAWRGB = 0x200,
FBZ_DRAW_FRONT = 0x0000,
FBZ_DRAW_BACK = 0x4000,
FBZ_DRAW_MASK = 0xc000
};
static int voodoo_reads = 0;
static void voodoo_recalc()
{
uint32_t buffer_offset = ((voodoo.fbiInit2 >> 11) & 511) * 4096;
voodoo.front_offset = 0;
voodoo.back_offset = buffer_offset;
voodoo.aux_offset = buffer_offset * 2;
switch (voodoo.lfbMode & LFB_WRITE_MASK)
{
case LFB_WRITE_FRONT:
voodoo.fb_write_offset = voodoo.front_offset;
break;
case LFB_WRITE_BACK:
voodoo.fb_write_offset = voodoo.back_offset;
break;
default:
fatal("voodoo_recalc : unknown lfb destination\n");
}
switch (voodoo.lfbMode & LFB_READ_MASK)
{
case LFB_READ_FRONT:
voodoo.fb_read_offset = voodoo.front_offset;
break;
case LFB_READ_BACK:
voodoo.fb_read_offset = voodoo.back_offset;
break;
case LFB_READ_AUX:
voodoo.fb_read_offset = voodoo.aux_offset;
break;
default:
fatal("voodoo_recalc : unknown lfb source\n");
}
switch (voodoo.fbzMode & FBZ_DRAW_MASK)
{
case FBZ_DRAW_FRONT:
voodoo.draw_offset = voodoo.front_offset;
break;
case FBZ_DRAW_BACK:
voodoo.draw_offset = voodoo.back_offset;
break;
default:
fatal("voodoo_recalc : unknown draw buffer\n");
}
voodoo.row_width = ((voodoo.fbiInit1 >> 4) & 15) * 64 * 2;
pclog("voodoo_recalc : front_offset %08X back_offset %08X aux_offset %08X\n", voodoo.front_offset, voodoo.back_offset, voodoo.aux_offset);
pclog(" fb_read_offset %08X fb_write_offset %08X row_width %i\n", voodoo.fb_read_offset, voodoo.fb_write_offset, voodoo.row_width);
}
static int dither_matrix_4x4[16] =
{
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5
};
static int dither_rb[256][4][4];
static int dither_g[256][4][4];
static void voodoo_make_dither()
{
int c, x, y;
int seen_rb[1024];
int seen_g[1024];
for (c = 0; c < 256; c++)
{
int rb = (int)(((double)c * 496.0) / 256.0);
int g = (int)(((double)c * 1008.0) / 256.0);
pclog("rb %i %i\n", rb, c);
for (y = 0; y < 4; y++)
{
for (x = 0; x < 4; x++)
{
int val;
val = rb + dither_matrix_4x4[x + (y << 2)];
dither_rb[c][y][x] = val >> 4;
pclog("RB %i %i, %i %i %i %i\n", c, x, y, val, val >> 4, dither_rb[c][y][x]);
if (dither_rb[c][y][x] > 31)
fatal("RB overflow %i %i %i %i\n", c, x, y, dither_rb[c][y][x]);
val = g + dither_matrix_4x4[x + (y << 2)];
dither_g[c][y][x] = val >> 4;
if (dither_g[c][y][x] > 63)
fatal("G overflow %i %i %i %i\n", c, x, y, dither_g[c][y][x]);
}
}
}
memset(seen_rb, 0, sizeof(seen_rb));
memset(seen_g, 0, sizeof(seen_g));
for (c = 0; c < 256; c++)
{
int total_rb = 0;
int total_g = 0;
for (y = 0; y < 4; y++)
{
for (x = 0; x < 4; x++)
{
total_rb += dither_rb[c][y][x];
pclog("total_rb %i %i, %i %i %i\n", c, x, y, total_rb, dither_rb[c][y][x]);
total_g += dither_g[c][y][x];
}
}
if (seen_rb[total_rb])
fatal("Duplicate rb %i %i\n", c, total_rb);
seen_rb[total_rb] = 1;
if (seen_g[total_g])
fatal("Duplicate g %i %i\n", c, total_g);
seen_g[total_g] = 1;
}
}
static void voodoo_triangle(struct voodoo *voodoo)
{
int y, yend, ydir;
int xstart, xend, xdir;
int dx1, dx2;
pclog("voodoo_triangle %i : vA %f, %f vB %f, %f vC %f, %f\n", tris, (float)voodoo->vertexAx / 16.0, (float)voodoo->vertexAy / 16.0,
(float)voodoo->vertexBx / 16.0, (float)voodoo->vertexBy / 16.0,
(float)voodoo->vertexCx / 16.0, (float)voodoo->vertexCy / 16.0);
// output = 3;
tris++;
// if (tris == 3)
// fatal("tris 2\n");
y = voodoo->vertexAy >> 4;
ydir = (voodoo->vertexAy >= voodoo->vertexCy) ? -1 : 1;
if (voodoo->vertexAy == voodoo->vertexBy)
{
xstart = voodoo->vertexAx << 8;
xend = voodoo->vertexBx << 8;
}
else
xstart = xend = voodoo->vertexAx << 8;
xdir = (xstart >= xend) ? -1 : 1;
if (voodoo->vertexAy != voodoo->vertexBy)
{
/*Draw top half*/
dx1 = (int)(((voodoo->vertexBx << 8) - xstart) << 4) / (int)((voodoo->vertexBy - voodoo->vertexAy) * ydir);
dx2 = (int)(((voodoo->vertexCx << 8) - xend) << 4) / (int)((voodoo->vertexCy - voodoo->vertexAy) * ydir);
yend = voodoo->vertexBy >> 4;
pclog("voodoo_triangle : top-half %X %X %X %X %i %i %i %i\n", xstart, xend, dx1, dx2, xdir, y, yend, ydir);
for (; y != yend; y += ydir)
{
pclog(" %i : %i - %i\n", xstart >> 12, xend >> 12);
xstart += dx1;
xend += dx2;
}
}
if (voodoo->vertexBy != voodoo->vertexCy)
{
/*Draw bottom half*/
dx1 = (int)(((voodoo->vertexCx << 8) - xstart) << 4) / (int)((voodoo->vertexCy - voodoo->vertexAy) * ydir);
dx2 = (int)(((voodoo->vertexCx << 8) - xend) << 4) / (int)((voodoo->vertexCy - voodoo->vertexAy) * ydir);
yend = voodoo->vertexCy >> 4;
pclog("voodoo_triangle : bottom-half %X %X %X %X %X %i %i %i %i\n", xstart, xend, dx1, dx2, dx2 * 36, xdir, y, yend, ydir);
for (; y != yend; y += ydir)
{
int x = xstart >> 12, x2 = xend >> 12;
pclog(" %i : %i - %i\n", y, x, x2);
for (; x != x2; x += xdir)
{
if (voodoo->fbzMode & FBZ_DRAWRGB)
{
int r = (voodoo->color1 >> 16) & 0xff;
int g = (voodoo->color1 >> 8) & 0xff;
int b = voodoo->color1 & 0xff;
if (voodoo->fbzMode & FBZ_DITHER)
{
r = dither_rb[r][y & 3][x & 3];
g = dither_g[g][y & 3][x & 3];
b = dither_rb[b][y & 3][x & 3];
/* r = ((r << 1) + dither_matrix_4x4[(x & 3) + ((y & 3) << 2)]) >> 4;
g = ((g << 2) + dither_matrix_4x4[(x & 3) + ((y & 3) << 2)]) >> 4;
b = ((b << 1) + dither_matrix_4x4[(x & 3) + ((y & 3) << 2)]) >> 4;
if (r > 31) r = 31;
if (g > 63) g = 63;
if (b > 31) b = 31;*/
/* r = (((r << 1) - (r >> 4) + (r >> 7) + dither_matrix_4x4[(x & 3) + ((y & 3) << 2)]) >> 1) >> 3;
g = (((g << 2) - (g >> 4) + (g >> 6) + dither_matrix_4x4[(x & 3) + ((y & 3) << 2)]) >> 2) >> 2;
b = (((b << 1) - (b >> 4) + (b >> 7) + dither_matrix_4x4[(x & 3) + ((y & 3) << 2)]) >> 1) >> 3;*/
}
else
{
r >>= 3;
g >>= 2;
b >>= 3;
}
*(uint16_t *)(&voodoo->fb_mem[voodoo->draw_offset + (y * voodoo->row_width) + (x << 1)]) = b | (g << 5) | (r << 11);
pclog(" %i = %i %i %i %04X %08X %08X\n", x, r, g, b, b | (g << 5) | (r << 11), voodoo->draw_offset + (y * voodoo->row_width) + (x << 1), *(uint32_t *)(&voodoo->fb_mem[4]));
}
}
xstart += dx1;
xend += dx2;
}
}
/* for (y = ystart, y != yend; y += ydir)
{
if (voodoo.fbzMode & FBZ_DITHER
}*/
}
static uint32_t voodoo_readl(uint32_t addr, void *priv)
{
uint32_t temp;
switch (addr & 0x3fc)
{
case SST_status:
temp = 0x0ffff03f; /*FIFOs empty*/
break;
case SST_lfbMode:
return voodoo.lfbMode;
case SST_fbiInit4:
temp = voodoo.fbiInit4;
break;
case SST_fbiInit0:
temp = voodoo.fbiInit0;
break;
case SST_fbiInit1:
temp = voodoo.fbiInit1 & ~5; /*Pass-thru board with one SST-1*/
break;
case SST_fbiInit2:
if (voodoo.initEnable & 0x04)
temp = voodoo.dac_readdata;
else
temp = voodoo.fbiInit2;
break;
case SST_fbiInit3:
temp = voodoo.fbiInit3;
break;
default:
fatal("voodoo_readl : bad addr %08X\n", addr);
temp = 0xffffffff;
}
pclog("voodoo_readl : addr %08X val %08X %04X(%08X):%08X %i\n", addr, temp, CS, cs, pc, voodoo_reads++);
// if (voodoo_reads == 200494)
// output = 3;
return temp;
}
static void voodoo_writel(uint32_t addr, uint32_t val, void *priv)
{
pclog("voodoo_writel : addr %08X val %08X %04X(%08X):%08X\n", addr, val, CS, cs, pc);
switch (addr & 0x3fc)
{
case SST_swapbufferCMD:
pclog(" start swap buffer command\n");
// output = 3;
break;
case SST_vertexAx:
voodoo.vertexAx = val & 0xffff;
break;
case SST_vertexAy:
voodoo.vertexAy = val & 0xffff;
break;
case SST_vertexBx:
voodoo.vertexBx = val & 0xffff;
break;
case SST_vertexBy:
voodoo.vertexBy = val & 0xffff;
break;
case SST_vertexCx:
voodoo.vertexCx = val & 0xffff;
break;
case SST_vertexCy:
voodoo.vertexCy = val & 0xffff;
break;
case SST_startR:
voodoo.startR = val & 0xffffff;
break;
case SST_startG:
voodoo.startG = val & 0xffffff;
break;
case SST_startB:
voodoo.startB = val & 0xffffff;
break;
case SST_startZ:
voodoo.startZ = val;
break;
case SST_startA:
voodoo.startA = val & 0xffffff;
break;
case SST_startS:
voodoo.startS = val;
break;
case SST_startT:
voodoo.startT = val;
break;
case SST_startW:
voodoo.startW = val;
break;
case SST_dRdX:
voodoo.dRdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
break;
case SST_dGdX:
voodoo.dGdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
break;
case SST_dBdX:
voodoo.dBdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
break;
case SST_dZdX:
voodoo.dZdX = val;
break;
case SST_dAdX:
voodoo.dAdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
break;
case SST_dSdX:
voodoo.dSdX = val;
break;
case SST_dTdX:
voodoo.dTdX = val;
break;
case SST_dWdX:
voodoo.dWdX = val;
break;
case SST_dRdY:
voodoo.dRdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
break;
case SST_dGdY:
voodoo.dGdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
break;
case SST_dBdY:
voodoo.dBdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
break;
case SST_dZdY:
voodoo.dZdY = val;
break;
case SST_dAdY:
voodoo.dAdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0);
break;
case SST_dSdY:
voodoo.dSdY = val;
break;
case SST_dTdY:
voodoo.dTdY = val;
break;
case SST_dWdY:
voodoo.dWdY = val;
break;
case SST_triangleCMD:
voodoo_triangle(&voodoo);
break;
case SST_fbzMode:
voodoo.fbzMode = val;
break;
case SST_lfbMode:
voodoo.lfbMode = val;
voodoo_recalc();
break;
case SST_color0:
voodoo.color0 = val;
break;
case SST_color1:
voodoo.color1 = val;
break;
case SST_fbiInit4:
if (voodoo.initEnable & 0x01)
voodoo.fbiInit4 = val;
break;
case SST_fbiInit0:
if (voodoo.initEnable & 0x01)
voodoo.fbiInit0 = val;
break;
case SST_fbiInit1:
if (voodoo.initEnable & 0x01)
voodoo.fbiInit1 = val;
break;
case SST_fbiInit2:
if (voodoo.initEnable & 0x01)
{
voodoo.fbiInit2 = val;
voodoo_recalc();
}
break;
case SST_fbiInit3:
if (voodoo.initEnable & 0x01)
voodoo.fbiInit3 = val;
break;
case SST_dacData:
voodoo.dac_reg = (val >> 8) & 7;
voodoo.dac_readdata = 0xff;
if (val & 0x800)
{
pclog(" dacData read %i %02X\n", voodoo.dac_reg, voodoo.dac_data[7]);
if (voodoo.dac_reg == 5)
{
switch (voodoo.dac_data[7])
{
case 0x01: voodoo.dac_readdata = 0x55; break;
case 0x07: voodoo.dac_readdata = 0x71; break;
case 0x0b: voodoo.dac_readdata = 0x79; break;
}
}
else
voodoo.dac_readdata = voodoo.dac_data[voodoo.dac_readdata];
}
else
voodoo.dac_data[voodoo.dac_reg] = val & 0xff;
break;
}
}
static uint16_t voodoo_fb_readw(uint32_t addr, void *priv)
{
pclog("voodoo_fb_readw : %08X %04X\n", addr, *(uint16_t *)(&voodoo.fb_mem[addr & 0x1fffff]));
return *(uint16_t *)(&voodoo.fb_mem[addr & 0x1fffff]);
}
static uint32_t voodoo_fb_readl(uint32_t addr, void *priv)
{
int x, y;
uint32_t read_addr;
uint32_t temp;
x = (addr >> 1) & 0x3ff;
y = (addr >> 11) & 0x3ff;
read_addr = voodoo.fb_read_offset + (x << 1) + (y * voodoo.row_width);
temp = *(uint32_t *)(&voodoo.fb_mem[read_addr & 0x1fffff]);
pclog("voodoo_fb_readl : %08X %08X %i %i %08X %08X\n", addr, temp, x, y, read_addr, *(uint32_t *)(&voodoo.fb_mem[4]));
return temp;
}
static void voodoo_fb_writew(uint32_t addr, uint16_t val, void *priv)
{
pclog("voodoo_fb_writew : %08X %04X\n", addr, val);
*(uint16_t *)(&voodoo.fb_mem[addr & 0x1fffff]) = val;
}
static void voodoo_fb_writel(uint32_t addr, uint32_t val, void *priv)
{
int x, y;
uint32_t write_addr, write_addr_aux;
uint32_t colour_data, depth_data;
int write_mask, count = 1;
pclog("voodoo_fb_writel : %08X %08X\n", addr, val);
if (voodoo.lfbMode & 0x100)
fatal("voodoo_fb_writel : using pixel processing\n");
switch (voodoo.lfbMode & LFB_FORMAT_MASK)
{
case LFB_FORMAT_RGB565:
colour_data = val;
write_mask = LFB_WRITE_COLOUR;
break;
case LFB_FORMAT_DEPTH:
depth_data = val;
write_mask = LFB_WRITE_DEPTH;
addr <<= 1;
count = 2;
break;
default:
fatal("voodoo_fb_writel : bad LFB format %08X\n", voodoo.lfbMode);
}
x = addr & 0x3fe;
y = (addr >> 10) & 0x3ff;
write_addr = voodoo.fb_read_offset + x + (y * voodoo.row_width);
write_addr_aux = voodoo.aux_offset + x + (y * voodoo.row_width);
while (count--)
{
if (write_mask & LFB_WRITE_COLOUR)
*(uint16_t *)(&voodoo.fb_mem[write_addr & 0x1ffffe]) = colour_data;
if (write_mask & LFB_WRITE_DEPTH)
*(uint16_t *)(&voodoo.fb_mem[write_addr_aux & 0x1ffffe]) = depth_data;
colour_data >>= 16;
depth_data >>= 16;
write_addr += 2;
write_addr_aux += 2;
}
}
static void voodoo_tex_writew(uint32_t addr, uint16_t val, void *priv)
{
pclog("voodoo_tex_write : %08X %04X\n", addr, val);
*(uint16_t *)(&voodoo.tex_mem[addr & 0x1fffff]) = val;
}
static void voodoo_tex_writel(uint32_t addr, uint32_t val, void *priv)
{
pclog("voodoo_tex_write : %08X %08X\n", addr, val);
*(uint32_t *)(&voodoo.tex_mem[addr & 0x1fffff]) = val;
}
static void voodoo_recalcmapping()
{
if (voodoo.pci_enable)
{
pclog("voodoo_recalcmapping : memBaseAddr %08X\n", voodoo.memBaseAddr);
mem_mapping_set_addr(&voodoo.mmio_mapping, voodoo.memBaseAddr, 0x00400000);
mem_mapping_set_addr(&voodoo.fb_mapping, voodoo.memBaseAddr + 0x00400000, 0x00400000);
mem_mapping_set_addr(&voodoo.tex_mapping, voodoo.memBaseAddr + 0x00800000, 0x00800000);
}
else
{
pclog("voodoo_recalcmapping : disabled\n");
mem_mapping_disable(&voodoo.mmio_mapping);
mem_mapping_disable(&voodoo.fb_mapping);
mem_mapping_disable(&voodoo.tex_mapping);
}
}
uint8_t voodoo_pci_read(int func, int addr, void *priv)
{
pclog("Voodoo PCI read %08X\n", addr);
switch (addr)
{
case 0x00: return 0x1a; /*3dfx*/
case 0x01: return 0x12;
case 0x02: return 0x01; /*SST-1 (Voodoo Graphics)*/
case 0x03: return 0x00;
case 0x04: return voodoo.pci_enable ? 0x02 : 0x00; /*Respond to memory accesses*/
case 0x08: return 0; /*Revision ID*/
case 0x09: return 0; /*Programming interface*/
case 0x10: return 0x00; /*memBaseAddr*/
case 0x11: return 0x00;
case 0x12: return 0x00;
case 0x13: return voodoo.memBaseAddr >> 24;
case 0x40: return voodoo.initEnable;
}
return 0;
}
void voodoo_pci_write(int func, int addr, uint8_t val, void *priv)
{
pclog("Voodoo PCI write %04X %02X\n", addr, val);
switch (addr)
{
case 0x04: voodoo.pci_enable = val & 2; voodoo_recalcmapping(); break;
case 0x13: voodoo.memBaseAddr = val << 24; voodoo_recalcmapping(); break;
case 0x40: voodoo.initEnable = val; break;
}
}
void voodoo_init()
{
return;
voodoo_make_dither();
pci_add(voodoo_pci_read, voodoo_pci_write, NULL);
mem_mapping_add(&voodoo.mmio_mapping, 0, 0, NULL, NULL, voodoo_readl, NULL, NULL, voodoo_writel, NULL);
mem_mapping_add(&voodoo.fb_mapping, 0, 0, NULL, voodoo_fb_readw, voodoo_fb_readl, NULL, voodoo_fb_writew, voodoo_fb_writel, NULL);
mem_mapping_add(&voodoo.tex_mapping, 0, 0, NULL, NULL, NULL, NULL, voodoo_tex_writew, voodoo_tex_writel, NULL);
voodoo.fb_mem = malloc(2 * 1024 * 1024);
voodoo.tex_mem = malloc(2 * 1024 * 1024);
}