Files
86Box/src/video/vid_voodoo.c
TC1995 0ba20993de Voodoo 3D change of the day (June 16th, 2025)
When 3D is enabled on the behalf of the Voodoo, make sure the override is reflected in recalctimings as well so that it can use the old calculation way of the overscan. Fixes more blackness in some areas of some games (and possibly more).
2025-06-16 21:14:31 +02:00

1446 lines
51 KiB
C

/*
* 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.
*
* Voodoo Graphics, 2, Banshee, 3 emulation.
*
*
*
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* leilei
*
* Copyright 2008-2020 Sarah Walker.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <wchar.h>
#include <math.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include "cpu.h"
#include <86box/machine.h>
#include <86box/device.h>
#include <86box/mem.h>
#include <86box/pci.h>
#include <86box/rom.h>
#include <86box/timer.h>
#include <86box/device.h>
#include <86box/plat.h>
#include <86box/thread.h>
#include <86box/video.h>
#include <86box/vid_svga.h>
#include <86box/vid_voodoo_common.h>
#include <86box/vid_voodoo_blitter.h>
#include <86box/vid_voodoo_display.h>
#include <86box/vid_voodoo_dither.h>
#include <86box/vid_voodoo_fb.h>
#include <86box/vid_voodoo_fifo.h>
#include <86box/vid_voodoo_reg.h>
#include <86box/vid_voodoo_regs.h>
#include <86box/vid_voodoo_render.h>
#include <86box/vid_voodoo_texture.h>
rgba8_t rgb332[0x100];
rgba8_t ai44[0x100];
rgba8_t rgb565[0x10000];
rgba8_t argb1555[0x10000];
rgba8_t argb4444[0x10000];
rgba8_t ai88[0x10000];
int tris = 0;
#ifdef ENABLE_VOODOO_LOG
int voodoo_do_log = ENABLE_VOODOO_LOG;
static void
voodoo_log(const char *fmt, ...)
{
va_list ap;
if (voodoo_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define voodoo_log(fmt, ...)
#endif
void
voodoo_recalc(voodoo_t *voodoo)
{
uint32_t buffer_offset = ((voodoo->fbiInit2 >> 11) & 511) * 4096;
if (voodoo->type >= VOODOO_BANSHEE)
return;
voodoo->params.front_offset = voodoo->disp_buffer * buffer_offset;
voodoo->back_offset = voodoo->draw_buffer * buffer_offset;
voodoo->buffer_cutoff = TRIPLE_BUFFER ? (buffer_offset * 4) : (buffer_offset * 3);
if (TRIPLE_BUFFER)
voodoo->params.aux_offset = buffer_offset * 3;
else
voodoo->params.aux_offset = buffer_offset * 2;
switch (voodoo->lfbMode & LFB_WRITE_MASK) {
case LFB_WRITE_FRONT:
voodoo->fb_write_offset = voodoo->params.front_offset;
voodoo->fb_write_buffer = voodoo->disp_buffer;
break;
case LFB_WRITE_BACK:
voodoo->fb_write_offset = voodoo->back_offset;
voodoo->fb_write_buffer = voodoo->draw_buffer;
break;
default:
/*BreakNeck sets invalid LFB write buffer select*/
voodoo->fb_write_offset = voodoo->params.front_offset;
break;
}
switch (voodoo->lfbMode & LFB_READ_MASK) {
case LFB_READ_FRONT:
voodoo->fb_read_offset = voodoo->params.front_offset;
break;
case LFB_READ_BACK:
voodoo->fb_read_offset = voodoo->back_offset;
break;
case LFB_READ_AUX:
voodoo->fb_read_offset = voodoo->params.aux_offset;
break;
default:
fatal("voodoo_recalc : unknown lfb source\n");
}
switch (voodoo->params.fbzMode & FBZ_DRAW_MASK) {
case FBZ_DRAW_FRONT:
voodoo->params.draw_offset = voodoo->params.front_offset;
voodoo->fb_draw_buffer = voodoo->disp_buffer;
break;
case FBZ_DRAW_BACK:
voodoo->params.draw_offset = voodoo->back_offset;
voodoo->fb_draw_buffer = voodoo->draw_buffer;
break;
default:
fatal("voodoo_recalc : unknown draw buffer\n");
}
voodoo->block_width = ((voodoo->fbiInit1 >> 4) & 15) * 2;
if (voodoo->fbiInit6 & (1 << 30))
voodoo->block_width += 1;
if (voodoo->fbiInit1 & (1 << 24))
voodoo->block_width += 32;
voodoo->row_width = voodoo->block_width * 32 * 2;
voodoo->params.row_width = voodoo->row_width;
voodoo->aux_row_width = voodoo->row_width;
voodoo->params.aux_row_width = voodoo->aux_row_width;
}
static uint16_t
voodoo_readw(uint32_t addr, void *priv)
{
voodoo_t *voodoo = (voodoo_t *) priv;
addr &= 0xffffff;
cycles -= voodoo->read_time;
if ((addr & 0xc00000) == 0x400000) /*Framebuffer*/
{
if (SLI_ENABLED) {
const voodoo_set_t *set = voodoo->set;
int y = (addr >> 11) & 0x3ff;
if (y & 1)
voodoo = set->voodoos[1];
else
voodoo = set->voodoos[0];
}
voodoo->flush = 1;
while (!FIFO_EMPTY) {
voodoo_wake_fifo_thread_now(voodoo);
thread_wait_event(voodoo->fifo_not_full_event, 1);
}
voodoo_wait_for_render_thread_idle(voodoo);
voodoo->flush = 0;
return voodoo_fb_readw(addr, voodoo);
}
return 0xffff;
}
static uint32_t
voodoo_readl(uint32_t addr, void *priv)
{
voodoo_t *voodoo = (voodoo_t *) priv;
uint32_t temp = 0xffffffff;
int fifo_size;
voodoo->rd_count++;
addr &= 0xffffff;
cycles -= voodoo->read_time;
if (addr & 0x800000) { /*Texture*/
} else if (addr & 0x400000) /*Framebuffer*/
{
if (SLI_ENABLED) {
const voodoo_set_t *set = voodoo->set;
int y = (addr >> 11) & 0x3ff;
if (y & 1)
voodoo = set->voodoos[1];
else
voodoo = set->voodoos[0];
}
voodoo->flush = 1;
while (!FIFO_EMPTY) {
voodoo_wake_fifo_thread_now(voodoo);
thread_wait_event(voodoo->fifo_not_full_event, 1);
}
voodoo_wait_for_render_thread_idle(voodoo);
voodoo->flush = 0;
temp = voodoo_fb_readl(addr, voodoo);
} else
switch (addr & 0x3fc) {
case SST_status:
{
int fifo_entries = FIFO_ENTRIES;
int swap_count = voodoo->swap_count;
int written = voodoo->cmd_written + voodoo->cmd_written_fifo + voodoo->cmd_written_fifo_2;
int busy = (written - voodoo->cmd_read) || (voodoo->cmdfifo_depth_rd != voodoo->cmdfifo_depth_wr);
if (SLI_ENABLED && voodoo->type != VOODOO_2) {
voodoo_t *voodoo_other = (voodoo == voodoo->set->voodoos[0]) ? voodoo->set->voodoos[1] : voodoo->set->voodoos[0];
int other_written = voodoo_other->cmd_written + voodoo_other->cmd_written_fifo + voodoo->cmd_written_fifo_2;
if (voodoo_other->swap_count > swap_count)
swap_count = voodoo_other->swap_count;
if ((voodoo_other->fifo_write_idx - voodoo_other->fifo_read_idx) > fifo_entries)
fifo_entries = voodoo_other->fifo_write_idx - voodoo_other->fifo_read_idx;
if ((other_written - voodoo_other->cmd_read) || (voodoo_other->cmdfifo_depth_rd != voodoo_other->cmdfifo_depth_wr))
busy = 1;
if (!voodoo_other->voodoo_busy)
voodoo_wake_fifo_thread(voodoo_other);
}
fifo_size = 0xffff - fifo_entries;
temp = fifo_size << 12;
if (fifo_size < 0x40)
temp |= fifo_size;
else
temp |= 0x3f;
if (swap_count < 7)
temp |= (swap_count << 28);
else
temp |= (7 << 28);
if (!voodoo->v_retrace)
temp |= 0x40;
if (busy)
temp |= 0x380; /*Busy*/
if (!voodoo->voodoo_busy)
voodoo_wake_fifo_thread(voodoo);
}
break;
case SST_fbzColorPath:
voodoo_flush(voodoo);
temp = voodoo->params.fbzColorPath;
break;
case SST_fogMode:
voodoo_flush(voodoo);
temp = voodoo->params.fogMode;
break;
case SST_alphaMode:
voodoo_flush(voodoo);
temp = voodoo->params.alphaMode;
break;
case SST_fbzMode:
voodoo_flush(voodoo);
temp = voodoo->params.fbzMode;
break;
case SST_lfbMode:
voodoo_flush(voodoo);
temp = voodoo->lfbMode;
break;
case SST_clipLeftRight:
voodoo_flush(voodoo);
temp = voodoo->params.clipRight | (voodoo->params.clipLeft << 16);
break;
case SST_clipLowYHighY:
voodoo_flush(voodoo);
temp = voodoo->params.clipHighY | (voodoo->params.clipLowY << 16);
break;
case SST_stipple:
voodoo_flush(voodoo);
temp = voodoo->params.stipple;
break;
case SST_color0:
voodoo_flush(voodoo);
temp = voodoo->params.color0;
break;
case SST_color1:
voodoo_flush(voodoo);
temp = voodoo->params.color1;
break;
case SST_fbiPixelsIn:
temp = voodoo->fbiPixelsIn & 0xffffff;
break;
case SST_fbiChromaFail:
temp = voodoo->fbiChromaFail & 0xffffff;
break;
case SST_fbiZFuncFail:
temp = voodoo->fbiZFuncFail & 0xffffff;
break;
case SST_fbiAFuncFail:
temp = voodoo->fbiAFuncFail & 0xffffff;
break;
case SST_fbiPixelsOut:
temp = voodoo->fbiPixelsOut & 0xffffff;
break;
case SST_fbiInit4:
temp = voodoo->fbiInit4;
break;
case SST_fbiInit0:
temp = voodoo->fbiInit0;
break;
case SST_fbiInit1:
temp = voodoo->fbiInit1;
break;
case SST_fbiInit2:
if (voodoo->initEnable & 0x04)
temp = voodoo->dac_readdata;
else
temp = voodoo->fbiInit2;
break;
case SST_fbiInit3:
temp = voodoo->fbiInit3 | (1 << 10) | (2 << 8);
break;
case SST_vRetrace:
temp = voodoo->line & 0x1fff;
break;
case SST_hvRetrace:
{
uint32_t line_time = (uint32_t) (voodoo->line_time >> 32);
uint32_t diff = (timer_get_ts_int(&voodoo->timer) > (tsc & 0xffffffff)) ? (timer_get_ts_int(&voodoo->timer) - (tsc & 0xffffffff)) : 0;
uint32_t pre_div = diff * voodoo->h_total;
uint32_t post_div = pre_div / line_time;
uint32_t h_pos = (voodoo->h_total - 1) - post_div;
if (h_pos >= voodoo->h_total)
h_pos = 0;
temp = voodoo->line & 0x1fff;
temp |= (h_pos << 16);
}
break;
case SST_fbiInit5:
temp = voodoo->fbiInit5 & ~0x1ff;
break;
case SST_fbiInit6:
temp = voodoo->fbiInit6;
break;
case SST_fbiInit7:
temp = voodoo->fbiInit7 & ~0xff;
break;
case SST_cmdFifoBaseAddr:
temp = voodoo->cmdfifo_base >> 12;
temp |= (voodoo->cmdfifo_end >> 12) << 16;
break;
case SST_cmdFifoRdPtr:
temp = voodoo->cmdfifo_rp;
break;
case SST_cmdFifoAMin:
temp = voodoo->cmdfifo_amin;
break;
case SST_cmdFifoAMax:
temp = voodoo->cmdfifo_amax;
break;
case SST_cmdFifoDepth:
temp = voodoo->cmdfifo_depth_wr - voodoo->cmdfifo_depth_rd;
break;
default:
voodoo_log("voodoo_readl : bad addr %08X\n", addr);
temp = 0xffffffff;
}
return temp;
}
static void
voodoo_writew(uint32_t addr, uint16_t val, void *priv)
{
voodoo_t *voodoo = (voodoo_t *) priv;
voodoo->wr_count++;
addr &= 0xffffff;
cycles -= voodoo->write_time;
if ((addr & 0xc00000) == 0x400000) /*Framebuffer*/
voodoo_queue_command(voodoo, addr | FIFO_WRITEW_FB, val);
}
static void
voodoo_writel(uint32_t addr, uint32_t val, void *priv)
{
voodoo_t *voodoo = (voodoo_t *) priv;
voodoo->wr_count++;
addr &= 0xffffff;
if (addr == voodoo->last_write_addr + 4)
cycles -= voodoo->burst_time;
else
cycles -= voodoo->write_time;
voodoo->last_write_addr = addr;
if (addr & 0x800000) /*Texture*/
{
voodoo->tex_count++;
voodoo_queue_command(voodoo, addr | FIFO_WRITEL_TEX, val);
} else if (addr & 0x400000) /*Framebuffer*/
{
voodoo_queue_command(voodoo, addr | FIFO_WRITEL_FB, val);
} else if ((addr & 0x200000) && (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE)) {
#if 0
voodoo_log("Write CMDFIFO %08x(%08x) %08x %08x\n", addr, voodoo->cmdfifo_base + (addr & 0x3fffc), val, (voodoo->cmdfifo_base + (addr & 0x3fffc)) & voodoo->fb_mask);
#endif
*(uint32_t *) &voodoo->fb_mem[(voodoo->cmdfifo_base + (addr & 0x3fffc)) & voodoo->fb_mask] = val;
voodoo->cmdfifo_depth_wr++;
if ((voodoo->cmdfifo_depth_wr - voodoo->cmdfifo_depth_rd) < 20)
voodoo_wake_fifo_thread(voodoo);
} else
switch (addr & 0x3fc) {
case SST_intrCtrl:
fatal("intrCtrl write %08x\n", val);
break;
case SST_userIntrCMD:
fatal("userIntrCMD write %08x\n", val);
break;
case SST_swapbufferCMD:
voodoo->cmd_written++;
thread_wait_mutex(voodoo->swap_mutex);
voodoo->swap_count++;
thread_release_mutex(voodoo->swap_mutex);
if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE)
return;
voodoo_queue_command(voodoo, addr | FIFO_WRITEL_REG, val);
if (!voodoo->voodoo_busy)
voodoo_wake_fifo_threads(voodoo->set, voodoo);
break;
case SST_triangleCMD:
if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE)
return;
voodoo->cmd_written++;
voodoo_queue_command(voodoo, addr | FIFO_WRITEL_REG, val);
if (!voodoo->voodoo_busy)
voodoo_wake_fifo_threads(voodoo->set, voodoo);
break;
case SST_ftriangleCMD:
if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE)
return;
voodoo->cmd_written++;
voodoo_queue_command(voodoo, addr | FIFO_WRITEL_REG, val);
if (!voodoo->voodoo_busy)
voodoo_wake_fifo_threads(voodoo->set, voodoo);
break;
case SST_fastfillCMD:
if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE)
return;
voodoo->cmd_written++;
voodoo_queue_command(voodoo, addr | FIFO_WRITEL_REG, val);
if (!voodoo->voodoo_busy)
voodoo_wake_fifo_threads(voodoo->set, voodoo);
break;
case SST_nopCMD:
if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE)
return;
voodoo->cmd_written++;
voodoo_queue_command(voodoo, addr | FIFO_WRITEL_REG, val);
if (!voodoo->voodoo_busy)
voodoo_wake_fifo_threads(voodoo->set, voodoo);
break;
case SST_fbiInit4:
if (voodoo->initEnable & 0x01) {
voodoo->fbiInit4 = val;
voodoo->read_time = pci_nonburst_time + pci_burst_time * ((voodoo->fbiInit4 & 1) ? 2 : 1);
#if 0
voodoo_log("fbiInit4 write %08x - read_time=%i\n", val, voodoo->read_time);
#endif
}
break;
case SST_backPorch:
voodoo->backPorch = val;
break;
case SST_videoDimensions:
voodoo->videoDimensions = val;
voodoo->h_disp = (val & 0xfff) + 1;
voodoo->v_disp = (val >> 16) & 0xfff;
if ((voodoo->v_disp == 386) || (voodoo->v_disp == 402) ||
(voodoo->v_disp == 482) || (voodoo->v_disp == 602))
voodoo->v_disp -= 2;
break;
case SST_fbiInit0:
if (voodoo->initEnable & 0x01) {
voodoo->fbiInit0 = val;
thread_wait_mutex(voodoo->force_blit_mutex);
voodoo->can_blit = (voodoo->fbiInit0 & FBIINIT0_VGA_PASS) ? 1 : 0;
if (!voodoo->can_blit)
voodoo->force_blit_count = 0;
thread_release_mutex(voodoo->force_blit_mutex);
if (voodoo->set->nr_cards == 2)
svga_set_override(voodoo->svga, (voodoo->set->voodoos[0]->fbiInit0 | voodoo->set->voodoos[1]->fbiInit0) & 1);
else
svga_set_override(voodoo->svga, val & 1);
if (val & FBIINIT0_GRAPHICS_RESET) {
/*Reset display/draw buffer selection. This may not actually
happen here on a real Voodoo*/
voodoo->disp_buffer = 0;
voodoo->draw_buffer = 1;
voodoo_recalc(voodoo);
voodoo->front_offset = voodoo->params.front_offset;
}
svga_recalctimings(voodoo->svga);
}
break;
case SST_fbiInit1:
if (voodoo->initEnable & 0x01) {
if ((voodoo->fbiInit1 & FBIINIT1_VIDEO_RESET) && !(val & FBIINIT1_VIDEO_RESET)) {
voodoo->line = 0;
thread_wait_mutex(voodoo->swap_mutex);
voodoo->swap_count = 0;
thread_release_mutex(voodoo->swap_mutex);
voodoo->retrace_count = 0;
}
voodoo->fbiInit1 = (val & ~5) | (voodoo->fbiInit1 & 5);
voodoo->write_time = pci_nonburst_time + pci_burst_time * ((voodoo->fbiInit1 & 2) ? 1 : 0);
voodoo->burst_time = pci_burst_time * ((voodoo->fbiInit1 & 2) ? 2 : 1);
#if 0
voodoo_log("fbiInit1 write %08x - write_time=%i burst_time=%i\n", val, voodoo->write_time, voodoo->burst_time);
#endif
}
break;
case SST_fbiInit2:
if (voodoo->initEnable & 0x01) {
voodoo->fbiInit2 = val;
voodoo_recalc(voodoo);
}
break;
case SST_fbiInit3:
if (voodoo->initEnable & 0x01)
voodoo->fbiInit3 = val;
break;
case SST_hSync:
voodoo->hSync = val;
voodoo->h_total = (val & 0xffff) + (val >> 16);
voodoo_pixelclock_update(voodoo);
break;
case SST_vSync:
voodoo->vSync = val;
voodoo->v_total = (val & 0xffff) + (val >> 16);
break;
case SST_clutData:
voodoo->clutData[(val >> 24) & 0x3f].b = val & 0xff;
voodoo->clutData[(val >> 24) & 0x3f].g = (val >> 8) & 0xff;
voodoo->clutData[(val >> 24) & 0x3f].r = (val >> 16) & 0xff;
if (val & 0x20000000) {
voodoo->clutData[(val >> 24) & 0x3f].b = 255;
voodoo->clutData[(val >> 24) & 0x3f].g = 255;
voodoo->clutData[(val >> 24) & 0x3f].r = 255;
}
voodoo->clutData_dirty = 1;
break;
case SST_dacData:
voodoo->dac_reg = (val >> 8) & 7;
voodoo->dac_readdata = 0xff;
if (val & 0x800) {
// voodoo_log(" 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;
default:
break;
}
} else
voodoo->dac_readdata = voodoo->dac_data[voodoo->dac_readdata & 7];
} else {
if (voodoo->dac_reg == 5) {
if (!voodoo->dac_reg_ff)
voodoo->dac_pll_regs[voodoo->dac_data[4] & 0xf] = (voodoo->dac_pll_regs[voodoo->dac_data[4] & 0xf] & 0xff00) | val;
else
voodoo->dac_pll_regs[voodoo->dac_data[4] & 0xf] = (voodoo->dac_pll_regs[voodoo->dac_data[4] & 0xf] & 0xff) | (val << 8);
#if 0
voodoo_log("Write PLL reg %x %04x\n", voodoo->dac_data[4] & 0xf, voodoo->dac_pll_regs[voodoo->dac_data[4] & 0xf]);
#endif
voodoo->dac_reg_ff = !voodoo->dac_reg_ff;
if (!voodoo->dac_reg_ff)
voodoo->dac_data[4]++;
} else {
voodoo->dac_data[voodoo->dac_reg] = val & 0xff;
voodoo->dac_reg_ff = 0;
}
voodoo_pixelclock_update(voodoo);
}
break;
case SST_scrFilter:
if (voodoo->initEnable & 0x01) {
voodoo->scrfilterEnabled = 1;
voodoo->scrfilterThreshold = val; /* update the threshold values and generate a new lookup table if necessary */
if (val < 1)
voodoo->scrfilterEnabled = 0;
voodoo_threshold_check(voodoo);
voodoo_log("Voodoo Filter: %06x\n", val);
}
break;
case SST_fbiInit5:
if (voodoo->initEnable & 0x01)
voodoo->fbiInit5 = (val & ~0x41e6) | (voodoo->fbiInit5 & 0x41e6);
break;
case SST_fbiInit6:
if (voodoo->initEnable & 0x01)
voodoo->fbiInit6 = val;
break;
case SST_fbiInit7:
if (voodoo->initEnable & 0x01) {
voodoo->fbiInit7 = val;
voodoo->cmdfifo_enabled = val & 0x100;
}
break;
case SST_cmdFifoBaseAddr:
voodoo->cmdfifo_base = (val & 0x3ff) << 12;
voodoo->cmdfifo_end = ((val >> 16) & 0x3ff) << 12;
#if 0
voodoo_log("CMDFIFO base=%08x end=%08x\n", voodoo->cmdfifo_base, voodoo->cmdfifo_end);
#endif
break;
case SST_cmdFifoRdPtr:
voodoo->cmdfifo_rp = val;
break;
case SST_cmdFifoAMin:
voodoo->cmdfifo_amin = val;
break;
case SST_cmdFifoAMax:
voodoo->cmdfifo_amax = val;
break;
case SST_cmdFifoDepth:
voodoo->cmdfifo_depth_rd = 0;
voodoo->cmdfifo_depth_wr = val & 0xffff;
break;
default:
if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE) {
voodoo_log("Unknown register write in CMDFIFO mode %08x %08x\n", addr, val);
} else {
voodoo_queue_command(voodoo, addr | FIFO_WRITEL_REG, val);
}
break;
}
}
static uint16_t
voodoo_snoop_readw(uint32_t addr, void *priv)
{
const voodoo_set_t *set = (voodoo_set_t *) priv;
return voodoo_readw(addr, set->voodoos[0]);
}
static uint32_t
voodoo_snoop_readl(uint32_t addr, void *priv)
{
const voodoo_set_t *set = (voodoo_set_t *) priv;
return voodoo_readl(addr, set->voodoos[0]);
}
static void
voodoo_snoop_writew(uint32_t addr, uint16_t val, void *priv)
{
const voodoo_set_t *set = (voodoo_set_t *) priv;
voodoo_writew(addr, val, set->voodoos[0]);
voodoo_writew(addr, val, set->voodoos[1]);
}
static void
voodoo_snoop_writel(uint32_t addr, uint32_t val, void *priv)
{
const voodoo_set_t *set = (voodoo_set_t *) priv;
voodoo_writel(addr, val, set->voodoos[0]);
voodoo_writel(addr, val, set->voodoos[1]);
}
static void
voodoo_recalcmapping(voodoo_set_t *set)
{
if (set->nr_cards == 2) {
if (set->voodoos[0]->pci_enable && set->voodoos[0]->memBaseAddr) {
if (set->voodoos[0]->type == VOODOO_2 && set->voodoos[1]->initEnable & (1 << 23)) {
voodoo_log("voodoo_recalcmapping (pri) with snoop : memBaseAddr %08X\n", set->voodoos[0]->memBaseAddr);
mem_mapping_disable(&set->voodoos[0]->mapping);
mem_mapping_set_addr(&set->snoop_mapping, set->voodoos[0]->memBaseAddr, 0x01000000);
} else if (set->voodoos[1]->pci_enable && (set->voodoos[0]->memBaseAddr == set->voodoos[1]->memBaseAddr)) {
voodoo_log("voodoo_recalcmapping (pri) (sec) same addr : memBaseAddr %08X\n", set->voodoos[0]->memBaseAddr);
mem_mapping_disable(&set->voodoos[0]->mapping);
mem_mapping_disable(&set->voodoos[1]->mapping);
mem_mapping_set_addr(&set->snoop_mapping, set->voodoos[0]->memBaseAddr, 0x01000000);
return;
} else {
voodoo_log("voodoo_recalcmapping (pri) : memBaseAddr %08X\n", set->voodoos[0]->memBaseAddr);
mem_mapping_disable(&set->snoop_mapping);
mem_mapping_set_addr(&set->voodoos[0]->mapping, set->voodoos[0]->memBaseAddr, 0x01000000);
}
} else {
voodoo_log("voodoo_recalcmapping (pri) : disabled\n");
mem_mapping_disable(&set->voodoos[0]->mapping);
}
if (set->voodoos[1]->pci_enable && set->voodoos[1]->memBaseAddr) {
voodoo_log("voodoo_recalcmapping (sec) : memBaseAddr %08X\n", set->voodoos[1]->memBaseAddr);
mem_mapping_set_addr(&set->voodoos[1]->mapping, set->voodoos[1]->memBaseAddr, 0x01000000);
} else {
voodoo_log("voodoo_recalcmapping (sec) : disabled\n");
mem_mapping_disable(&set->voodoos[1]->mapping);
}
} else {
voodoo_t *voodoo = set->voodoos[0];
if (voodoo->pci_enable && voodoo->memBaseAddr) {
voodoo_log("voodoo_recalcmapping : memBaseAddr %08X\n", voodoo->memBaseAddr);
mem_mapping_set_addr(&voodoo->mapping, voodoo->memBaseAddr, 0x01000000);
} else {
voodoo_log("voodoo_recalcmapping : disabled\n");
mem_mapping_disable(&voodoo->mapping);
}
}
}
uint8_t
voodoo_pci_read(int func, int addr, void *priv)
{
const voodoo_t *voodoo = (voodoo_t *) priv;
if (func)
return 0;
#if 0
voodoo_log("Voodoo PCI read %08X PC=%08x\n", addr, cpu_state.pc);
#endif
switch (addr) {
case 0x00:
return 0x1a; /*3Dfx*/
case 0x01:
return 0x12;
case 0x02:
if (voodoo->type == VOODOO_2)
return 0x02; /*Voodoo 2*/
else
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 2; /*Revision ID*/
case 0x09:
return 0; /*Programming interface*/
case 0x0a:
return 0;
case 0x0b:
return 0x04;
case 0x10:
return 0x00; /*memBaseAddr*/
case 0x11:
return 0x00;
case 0x12:
return 0x00;
case 0x13:
return voodoo->memBaseAddr >> 24;
case 0x40:
return voodoo->initEnable & 0xff;
case 0x41:
if (voodoo->type == VOODOO_2)
return 0x50 | ((voodoo->initEnable >> 8) & 0x0f);
return (voodoo->initEnable >> 8) & 0x0f;
case 0x42:
return (voodoo->initEnable >> 16) & 0xff;
case 0x43:
return (voodoo->initEnable >> 24) & 0xff;
default:
break;
}
return 0;
}
void
voodoo_pci_write(int func, int addr, uint8_t val, void *priv)
{
voodoo_t *voodoo = (voodoo_t *) priv;
if (func)
return;
#if 0
voodoo_log("Voodoo PCI write %04X %02X PC=%08x\n", addr, val, cpu_state.pc);
#endif
switch (addr) {
case 0x04:
voodoo->pci_enable = val & 2;
voodoo_recalcmapping(voodoo->set);
break;
case 0x13:
voodoo->memBaseAddr = val << 24;
voodoo_recalcmapping(voodoo->set);
break;
case 0x40:
voodoo->initEnable = (voodoo->initEnable & ~0x000000ff) | val;
break;
case 0x41:
voodoo->initEnable = (voodoo->initEnable & ~0x0000ff00) | (val << 8);
break;
case 0x42:
voodoo->initEnable = (voodoo->initEnable & ~0x00ff0000) | (val << 16);
voodoo_recalcmapping(voodoo->set);
break;
case 0x43:
voodoo->initEnable = (voodoo->initEnable & ~0xff000000) | (val << 24);
voodoo_recalcmapping(voodoo->set);
break;
default:
break;
}
}
static void
voodoo_speed_changed(void *priv)
{
const voodoo_set_t *voodoo_set = (voodoo_set_t *) priv;
voodoo_pixelclock_update(voodoo_set->voodoos[0]);
voodoo_set->voodoos[0]->read_time = pci_nonburst_time + pci_burst_time * ((voodoo_set->voodoos[0]->fbiInit4 & 1) ? 2 : 1);
voodoo_set->voodoos[0]->write_time = pci_nonburst_time + pci_burst_time * ((voodoo_set->voodoos[0]->fbiInit1 & 2) ? 1 : 0);
voodoo_set->voodoos[0]->burst_time = pci_burst_time * ((voodoo_set->voodoos[0]->fbiInit1 & 2) ? 2 : 1);
if (voodoo_set->nr_cards == 2) {
voodoo_pixelclock_update(voodoo_set->voodoos[1]);
voodoo_set->voodoos[1]->read_time = pci_nonburst_time + pci_burst_time * ((voodoo_set->voodoos[1]->fbiInit4 & 1) ? 2 : 1);
voodoo_set->voodoos[1]->write_time = pci_nonburst_time + pci_burst_time * ((voodoo_set->voodoos[1]->fbiInit1 & 2) ? 1 : 0);
voodoo_set->voodoos[1]->burst_time = pci_burst_time * ((voodoo_set->voodoos[1]->fbiInit1 & 2) ? 2 : 1);
}
#if 0
voodoo_log("Voodoo read_time=%i write_time=%i burst_time=%i %08x %08x\n", voodoo->read_time, voodoo->write_time, voodoo->burst_time, voodoo->fbiInit1, voodoo->fbiInit4);
#endif
}
static void
voodoo_force_blit(void *priv)
{
const voodoo_set_t *voodoo_set = (voodoo_set_t *) priv;
thread_wait_mutex(voodoo_set->voodoos[0]->force_blit_mutex);
if (voodoo_set->voodoos[0]->can_blit) {
voodoo_set->voodoos[0]->force_blit_count++;
}
thread_release_mutex(voodoo_set->voodoos[0]->force_blit_mutex);
if (voodoo_set->nr_cards == 2) {
thread_wait_mutex(voodoo_set->voodoos[1]->force_blit_mutex);
if (voodoo_set->voodoos[1]->can_blit) {
voodoo_set->voodoos[1]->force_blit_count++;
}
thread_release_mutex(voodoo_set->voodoos[1]->force_blit_mutex);
}
}
void *
voodoo_card_init(void)
{
int c;
voodoo_t *voodoo = malloc(sizeof(voodoo_t));
memset(voodoo, 0, sizeof(voodoo_t));
voodoo->bilinear_enabled = device_get_config_int("bilinear");
voodoo->dithersub_enabled = device_get_config_int("dithersub");
voodoo->scrfilter = device_get_config_int("dacfilter");
voodoo->texture_size = device_get_config_int("texture_memory");
voodoo->texture_mask = (voodoo->texture_size << 20) - 1;
voodoo->fb_size = device_get_config_int("framebuffer_memory");
voodoo->fb_mask = (voodoo->fb_size << 20) - 1;
voodoo->render_threads = device_get_config_int("render_threads");
voodoo->odd_even_mask = voodoo->render_threads - 1;
#ifndef NO_CODEGEN
voodoo->use_recompiler = device_get_config_int("recompiler");
#endif
voodoo->type = device_get_config_int("type");
switch (voodoo->type) {
case VOODOO_1:
voodoo->dual_tmus = 0;
break;
case VOODOO_SB50:
voodoo->dual_tmus = 1;
break;
case VOODOO_2:
voodoo->dual_tmus = 1;
break;
default:
break;
}
if (voodoo->type == VOODOO_2) /*generate filter lookup tables*/
voodoo_generate_filter_v2(voodoo);
else
voodoo_generate_filter_v1(voodoo);
pci_add_card(PCI_ADD_NORMAL, voodoo_pci_read, voodoo_pci_write, voodoo, &voodoo->pci_slot);
mem_mapping_add(&voodoo->mapping, 0, 0, NULL, voodoo_readw, voodoo_readl, NULL, voodoo_writew, voodoo_writel, NULL, MEM_MAPPING_EXTERNAL, voodoo);
voodoo->fb_mem = malloc(4 * 1024 * 1024);
voodoo->tex_mem[0] = malloc(voodoo->texture_size * 1024 * 1024);
if (voodoo->dual_tmus)
voodoo->tex_mem[1] = malloc(voodoo->texture_size * 1024 * 1024);
voodoo->tex_mem_w[0] = (uint16_t *) voodoo->tex_mem[0];
voodoo->tex_mem_w[1] = (uint16_t *) voodoo->tex_mem[1];
for (c = 0; c < TEX_CACHE_MAX; c++) {
voodoo->texture_cache[0][c].data = malloc((256 * 256 + 256 * 256 + 128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2) * 4);
voodoo->texture_cache[0][c].base = -1; /*invalid*/
voodoo->texture_cache[0][c].refcount = 0;
if (voodoo->dual_tmus) {
voodoo->texture_cache[1][c].data = malloc((256 * 256 + 256 * 256 + 128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2) * 4);
voodoo->texture_cache[1][c].base = -1; /*invalid*/
voodoo->texture_cache[1][c].refcount = 0;
}
}
timer_add(&voodoo->timer, voodoo_callback, voodoo, 1);
voodoo->svga = svga_get_pri();
voodoo->fbiInit0 = 0;
voodoo->wake_fifo_thread = thread_create_event();
voodoo->wake_render_thread[0] = thread_create_event();
voodoo->wake_render_thread[1] = thread_create_event();
voodoo->wake_render_thread[2] = thread_create_event();
voodoo->wake_render_thread[3] = thread_create_event();
voodoo->wake_main_thread = thread_create_event();
voodoo->fifo_not_full_event = thread_create_event();
voodoo->render_not_full_event[0] = thread_create_event();
voodoo->render_not_full_event[1] = thread_create_event();
voodoo->render_not_full_event[2] = thread_create_event();
voodoo->render_not_full_event[3] = thread_create_event();
voodoo->fifo_thread_run = 1;
voodoo->fifo_thread = thread_create(voodoo_fifo_thread, voodoo);
voodoo->render_thread_run[0] = 1;
voodoo->render_thread[0] = thread_create(voodoo_render_thread_1, voodoo);
if (voodoo->render_threads >= 2) {
voodoo->render_thread_run[1] = 1;
voodoo->render_thread[1] = thread_create(voodoo_render_thread_2, voodoo);
}
if (voodoo->render_threads == 4) {
voodoo->render_thread_run[2] = 1;
voodoo->render_thread[2] = thread_create(voodoo_render_thread_3, voodoo);
voodoo->render_thread_run[3] = 1;
voodoo->render_thread[3] = thread_create(voodoo_render_thread_4, voodoo);
}
voodoo->swap_mutex = thread_create_mutex();
timer_add(&voodoo->wake_timer, voodoo_wake_timer, (void *) voodoo, 0);
for (c = 0; c < 0x100; c++) {
rgb332[c].r = c & 0xe0;
rgb332[c].g = (c << 3) & 0xe0;
rgb332[c].b = (c << 6) & 0xc0;
rgb332[c].r = rgb332[c].r | (rgb332[c].r >> 3) | (rgb332[c].r >> 6);
rgb332[c].g = rgb332[c].g | (rgb332[c].g >> 3) | (rgb332[c].g >> 6);
rgb332[c].b = rgb332[c].b | (rgb332[c].b >> 2);
rgb332[c].b = rgb332[c].b | (rgb332[c].b >> 4);
rgb332[c].a = 0xff;
ai44[c].a = (c & 0xf0) | ((c & 0xf0) >> 4);
ai44[c].r = (c & 0x0f) | ((c & 0x0f) << 4);
ai44[c].g = ai44[c].b = ai44[c].r;
}
for (c = 0; c < 0x10000; c++) {
rgb565[c].r = (c >> 8) & 0xf8;
rgb565[c].g = (c >> 3) & 0xfc;
rgb565[c].b = (c << 3) & 0xf8;
rgb565[c].r |= (rgb565[c].r >> 5);
rgb565[c].g |= (rgb565[c].g >> 6);
rgb565[c].b |= (rgb565[c].b >> 5);
rgb565[c].a = 0xff;
argb1555[c].r = (c >> 7) & 0xf8;
argb1555[c].g = (c >> 2) & 0xf8;
argb1555[c].b = (c << 3) & 0xf8;
argb1555[c].r |= (argb1555[c].r >> 5);
argb1555[c].g |= (argb1555[c].g >> 5);
argb1555[c].b |= (argb1555[c].b >> 5);
argb1555[c].a = (c & 0x8000) ? 0xff : 0;
argb4444[c].a = (c >> 8) & 0xf0;
argb4444[c].r = (c >> 4) & 0xf0;
argb4444[c].g = c & 0xf0;
argb4444[c].b = (c << 4) & 0xf0;
argb4444[c].a |= (argb4444[c].a >> 4);
argb4444[c].r |= (argb4444[c].r >> 4);
argb4444[c].g |= (argb4444[c].g >> 4);
argb4444[c].b |= (argb4444[c].b >> 4);
ai88[c].a = (c >> 8);
ai88[c].r = c & 0xff;
ai88[c].g = c & 0xff;
ai88[c].b = c & 0xff;
}
#ifndef NO_CODEGEN
voodoo_codegen_init(voodoo);
#endif
voodoo->disp_buffer = 0;
voodoo->draw_buffer = 1;
voodoo->force_blit_count = 0;
voodoo->can_blit = 0;
voodoo->force_blit_mutex = thread_create_mutex();
return voodoo;
}
void *
voodoo_2d3d_card_init(int type)
{
int c;
voodoo_t *voodoo = malloc(sizeof(voodoo_t));
memset(voodoo, 0, sizeof(voodoo_t));
voodoo->bilinear_enabled = device_get_config_int("bilinear");
voodoo->dithersub_enabled = device_get_config_int("dithersub");
voodoo->scrfilter = device_get_config_int("dacfilter");
voodoo->render_threads = device_get_config_int("render_threads");
voodoo->odd_even_mask = voodoo->render_threads - 1;
#ifndef NO_CODEGEN
voodoo->use_recompiler = device_get_config_int("recompiler");
#endif
voodoo->type = type;
voodoo->dual_tmus = (type == VOODOO_3) ? 1 : 0;
/*generate filter lookup tables*/
voodoo_generate_filter_v2(voodoo);
for (c = 0; c < TEX_CACHE_MAX; c++) {
voodoo->texture_cache[0][c].data = malloc((256 * 256 + 256 * 256 + 128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2) * 4);
voodoo->texture_cache[0][c].base = -1; /*invalid*/
voodoo->texture_cache[0][c].refcount = 0;
if (voodoo->dual_tmus) {
voodoo->texture_cache[1][c].data = malloc((256 * 256 + 256 * 256 + 128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2) * 4);
voodoo->texture_cache[1][c].base = -1; /*invalid*/
voodoo->texture_cache[1][c].refcount = 0;
}
}
timer_add(&voodoo->timer, voodoo_callback, voodoo, 1);
voodoo->fbiInit0 = 0;
voodoo->wake_fifo_thread = thread_create_event();
voodoo->wake_render_thread[0] = thread_create_event();
voodoo->wake_render_thread[1] = thread_create_event();
voodoo->wake_render_thread[2] = thread_create_event();
voodoo->wake_render_thread[3] = thread_create_event();
voodoo->wake_main_thread = thread_create_event();
voodoo->fifo_not_full_event = thread_create_event();
voodoo->render_not_full_event[0] = thread_create_event();
voodoo->render_not_full_event[1] = thread_create_event();
voodoo->render_not_full_event[2] = thread_create_event();
voodoo->render_not_full_event[3] = thread_create_event();
voodoo->fifo_thread_run = 1;
voodoo->fifo_thread = thread_create(voodoo_fifo_thread, voodoo);
voodoo->render_thread_run[0] = 1;
voodoo->render_thread[0] = thread_create(voodoo_render_thread_1, voodoo);
if (voodoo->render_threads >= 2) {
voodoo->render_thread_run[1] = 1;
voodoo->render_thread[1] = thread_create(voodoo_render_thread_2, voodoo);
}
if (voodoo->render_threads == 4) {
voodoo->render_thread_run[2] = 1;
voodoo->render_thread[2] = thread_create(voodoo_render_thread_3, voodoo);
voodoo->render_thread_run[3] = 1;
voodoo->render_thread[3] = thread_create(voodoo_render_thread_4, voodoo);
}
voodoo->swap_mutex = thread_create_mutex();
timer_add(&voodoo->wake_timer, voodoo_wake_timer, (void *) voodoo, 0);
for (c = 0; c < 0x100; c++) {
rgb332[c].r = c & 0xe0;
rgb332[c].g = (c << 3) & 0xe0;
rgb332[c].b = (c << 6) & 0xc0;
rgb332[c].r = rgb332[c].r | (rgb332[c].r >> 3) | (rgb332[c].r >> 6);
rgb332[c].g = rgb332[c].g | (rgb332[c].g >> 3) | (rgb332[c].g >> 6);
rgb332[c].b = rgb332[c].b | (rgb332[c].b >> 2);
rgb332[c].b = rgb332[c].b | (rgb332[c].b >> 4);
rgb332[c].a = 0xff;
ai44[c].a = (c & 0xf0) | ((c & 0xf0) >> 4);
ai44[c].r = (c & 0x0f) | ((c & 0x0f) << 4);
ai44[c].g = ai44[c].b = ai44[c].r;
}
for (c = 0; c < 0x10000; c++) {
rgb565[c].r = (c >> 8) & 0xf8;
rgb565[c].g = (c >> 3) & 0xfc;
rgb565[c].b = (c << 3) & 0xf8;
rgb565[c].r |= (rgb565[c].r >> 5);
rgb565[c].g |= (rgb565[c].g >> 6);
rgb565[c].b |= (rgb565[c].b >> 5);
rgb565[c].a = 0xff;
argb1555[c].r = (c >> 7) & 0xf8;
argb1555[c].g = (c >> 2) & 0xf8;
argb1555[c].b = (c << 3) & 0xf8;
argb1555[c].r |= (argb1555[c].r >> 5);
argb1555[c].g |= (argb1555[c].g >> 5);
argb1555[c].b |= (argb1555[c].b >> 5);
argb1555[c].a = (c & 0x8000) ? 0xff : 0;
argb4444[c].a = (c >> 8) & 0xf0;
argb4444[c].r = (c >> 4) & 0xf0;
argb4444[c].g = c & 0xf0;
argb4444[c].b = (c << 4) & 0xf0;
argb4444[c].a |= (argb4444[c].a >> 4);
argb4444[c].r |= (argb4444[c].r >> 4);
argb4444[c].g |= (argb4444[c].g >> 4);
argb4444[c].b |= (argb4444[c].b >> 4);
ai88[c].a = (c >> 8);
ai88[c].r = c & 0xff;
ai88[c].g = c & 0xff;
ai88[c].b = c & 0xff;
}
#ifndef NO_CODEGEN
voodoo_codegen_init(voodoo);
#endif
voodoo->disp_buffer = 0;
voodoo->draw_buffer = 1;
voodoo->force_blit_count = 0;
voodoo->can_blit = 0;
voodoo->force_blit_mutex = thread_create_mutex();
return voodoo;
}
void *
voodoo_init(UNUSED(const device_t *info))
{
voodoo_set_t *voodoo_set = malloc(sizeof(voodoo_set_t));
uint32_t tmuConfig = 1;
int type;
memset(voodoo_set, 0, sizeof(voodoo_set_t));
type = device_get_config_int("type");
voodoo_set->nr_cards = device_get_config_int("sli") ? 2 : 1;
voodoo_set->voodoos[0] = voodoo_card_init();
voodoo_set->voodoos[0]->set = voodoo_set;
if (voodoo_set->nr_cards == 2) {
voodoo_set->voodoos[1] = voodoo_card_init();
voodoo_set->voodoos[1]->set = voodoo_set;
if (type == VOODOO_2) {
voodoo_set->voodoos[0]->fbiInit5 |= FBIINIT5_MULTI_CVG;
voodoo_set->voodoos[1]->fbiInit5 |= FBIINIT5_MULTI_CVG;
} else {
voodoo_set->voodoos[0]->fbiInit1 |= FBIINIT1_MULTI_SST;
voodoo_set->voodoos[1]->fbiInit1 |= FBIINIT1_MULTI_SST;
}
}
switch (type) {
case VOODOO_1:
if (voodoo_set->nr_cards == 2)
tmuConfig = 1 | (3 << 3);
else
tmuConfig = 1;
break;
case VOODOO_SB50:
if (voodoo_set->nr_cards == 2)
tmuConfig = 1 | (3 << 3) | (3 << 6) | (2 << 9);
else
tmuConfig = 1 | (3 << 6);
break;
case VOODOO_2:
tmuConfig = 1 | (3 << 6);
break;
default:
break;
}
voodoo_set->voodoos[0]->tmuConfig = tmuConfig;
if (voodoo_set->nr_cards == 2)
voodoo_set->voodoos[1]->tmuConfig = tmuConfig;
mem_mapping_add(&voodoo_set->snoop_mapping, 0, 0, NULL, voodoo_snoop_readw, voodoo_snoop_readl, NULL, voodoo_snoop_writew, voodoo_snoop_writel, NULL, MEM_MAPPING_EXTERNAL, voodoo_set);
return voodoo_set;
}
void
voodoo_card_close(voodoo_t *voodoo)
{
voodoo->fifo_thread_run = 0;
thread_set_event(voodoo->wake_fifo_thread);
thread_wait(voodoo->fifo_thread);
voodoo->render_thread_run[0] = 0;
thread_set_event(voodoo->wake_render_thread[0]);
thread_wait(voodoo->render_thread[0]);
if (voodoo->render_threads >= 2) {
voodoo->render_thread_run[1] = 0;
thread_set_event(voodoo->wake_render_thread[1]);
thread_wait(voodoo->render_thread[1]);
}
if (voodoo->render_threads == 4) {
voodoo->render_thread_run[2] = 0;
thread_set_event(voodoo->wake_render_thread[2]);
thread_wait(voodoo->render_thread[2]);
voodoo->render_thread_run[3] = 0;
thread_set_event(voodoo->wake_render_thread[3]);
thread_wait(voodoo->render_thread[3]);
}
thread_destroy_event(voodoo->fifo_not_full_event);
thread_destroy_event(voodoo->wake_main_thread);
thread_destroy_event(voodoo->wake_fifo_thread);
thread_destroy_event(voodoo->wake_render_thread[0]);
thread_destroy_event(voodoo->wake_render_thread[1]);
thread_destroy_event(voodoo->render_not_full_event[0]);
thread_destroy_event(voodoo->render_not_full_event[1]);
for (uint8_t c = 0; c < TEX_CACHE_MAX; c++) {
if (voodoo->dual_tmus)
free(voodoo->texture_cache[1][c].data);
free(voodoo->texture_cache[0][c].data);
}
#ifndef NO_CODEGEN
voodoo_codegen_close(voodoo);
#endif
if (voodoo->type < VOODOO_BANSHEE && voodoo->fb_mem) {
free(voodoo->fb_mem);
if (voodoo->dual_tmus)
free(voodoo->tex_mem[1]);
free(voodoo->tex_mem[0]);
}
thread_close_mutex(voodoo->force_blit_mutex);
free(voodoo);
}
void
voodoo_close(void *priv)
{
voodoo_set_t *voodoo_set = (voodoo_set_t *) priv;
if (voodoo_set->nr_cards == 2)
voodoo_card_close(voodoo_set->voodoos[1]);
voodoo_card_close(voodoo_set->voodoos[0]);
free(voodoo_set);
}
static const device_config_t voodoo_config[] = {
// clang-format off
{
.name = "type",
.description = "Voodoo type",
.type = CONFIG_SELECTION,
.default_int = 0,
.file_filter = NULL,
.spinner = { 0 },
.selection = {
{ .description = "3Dfx Voodoo Graphics", .value = VOODOO_1 },
{ .description = "Obsidian SB50 + Amethyst (2 TMUs)", .value = VOODOO_SB50 },
{ .description = "3Dfx Voodoo 2", .value = VOODOO_2 },
{ .description = "" }
},
.bios = { { 0 } }
},
{
.name = "framebuffer_memory",
.description = "Framebuffer memory size",
.type = CONFIG_SELECTION,
.default_string = NULL,
.default_int = 2,
.file_filter = NULL,
.spinner = { 0 },
.selection = {
{ .description = "2 MB", .value = 2 },
{ .description = "4 MB", .value = 4 },
{ .description = "" }
},
.bios = { { 0 } }
},
{
.name = "texture_memory",
.description = "Texture memory size",
.type = CONFIG_SELECTION,
.default_string = NULL,
.default_int = 2,
.file_filter = NULL,
.spinner = { 0 },
.selection = {
{ .description = "2 MB", .value = 2 },
{ .description = "4 MB", .value = 4 },
{ .description = "" }
},
.bios = { { 0 } }
},
{
.name = "bilinear",
.description = "Bilinear filtering",
.type = CONFIG_BINARY,
.default_string = NULL,
.default_int = 1,
.file_filter = NULL,
.spinner = { 0 },
.selection = { { 0 } },
.bios = { { 0 } }
},
{
.name = "dithersub",
.description = "Dither subtraction",
.type = CONFIG_BINARY,
.default_string = NULL,
.default_int = 1,
.file_filter = NULL,
.spinner = { 0 },
.selection = { { 0 } },
.bios = { { 0 } }
},
{
.name = "dacfilter",
.description = "Screen Filter",
.type = CONFIG_BINARY,
.default_string = NULL,
.default_int = 0,
.file_filter = NULL,
.spinner = { 0 },
.selection = { { 0 } },
.bios = { { 0 } }
},
{
.name = "render_threads",
.description = "Render threads",
.type = CONFIG_SELECTION,
.default_string = NULL,
.default_int = 2,
.file_filter = NULL,
.spinner = { 0 },
.selection = {
{ .description = "1", .value = 1 },
{ .description = "2", .value = 2 },
{ .description = "4", .value = 4 },
{ .description = "" }
},
.bios = { { 0 } }
},
{
.name = "sli",
.description = "SLI",
.type = CONFIG_BINARY,
.default_string = NULL,
.default_int = 0,
.file_filter = NULL,
.spinner = { 0 },
.selection = { { 0 } },
.bios = { { 0 } }
},
#ifndef NO_CODEGEN
{
.name = "recompiler",
.description = "Dynamic Recompiler",
.type = CONFIG_BINARY,
.default_string = NULL,
.default_int = 1,
.file_filter = NULL,
.spinner = { 0 },
.selection = { { 0 } },
.bios = { { 0 } }
},
#endif
{ .name = "", .description = "", .type = CONFIG_END }
// clang-format on
};
const device_t voodoo_device = {
.name = "3Dfx Voodoo Graphics",
.internal_name = "voodoo",
.flags = DEVICE_PCI,
.local = 0,
.init = voodoo_init,
.close = voodoo_close,
.reset = NULL,
.available = NULL,
.speed_changed = voodoo_speed_changed,
.force_redraw = voodoo_force_blit,
.config = voodoo_config
};