#include #include #include "ibm.h" #include "device.h" #include "mem.h" #include "pci.h" #include "thread.h" #include "timer.h" #include "video.h" #include "vid_svga.h" #include "vid_voodoo.h" #include "vid_voodoo_dither.h" #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define CLAMP(x) (((x) < 0) ? 0 : (((x) > 0xff) ? 0xff : (x))) #define CLAMP16(x) (((x) < 0) ? 0 : (((x) > 0xffff) ? 0xffff : (x))) #define LOD_MAX 8 #define TEX_DIRTY_SHIFT 10 #define TEX_CACHE_MAX 64 enum { VOODOO_1 = 0, VOODOO_SB50 = 1, VOODOO_2 = 2 }; static uint32_t texture_offset[LOD_MAX+3] = { 0, 256*256, 256*256 + 128*128, 256*256 + 128*128 + 64*64, 256*256 + 128*128 + 64*64 + 32*32, 256*256 + 128*128 + 64*64 + 32*32 + 16*16, 256*256 + 128*128 + 64*64 + 32*32 + 16*16 + 8*8, 256*256 + 128*128 + 64*64 + 32*32 + 16*16 + 8*8 + 4*4, 256*256 + 128*128 + 64*64 + 32*32 + 16*16 + 8*8 + 4*4 + 2*2, 256*256 + 128*128 + 64*64 + 32*32 + 16*16 + 8*8 + 4*4 + 2*2 + 1*1, 256*256 + 128*128 + 64*64 + 32*32 + 16*16 + 8*8 + 4*4 + 2*2 + 1*1 + 1 }; static int tris = 0; static uint64_t status_time = 0; static uint64_t voodoo_time = 0; static int voodoo_render_time[2] = {0, 0}; static int voodoo_render_time_old[2] = {0, 0}; typedef union int_float { uint32_t i; float f; } int_float; typedef struct rgb_t { uint8_t b, g, r; uint8_t pad; } rgb_t; typedef struct rgba8_t { uint8_t b, g, r, a; } rgba8_t; typedef union rgba_u { struct { uint8_t b, g, r, a; } rgba; uint32_t u; } rgba_u; #define FIFO_SIZE 65536 #define FIFO_MASK (FIFO_SIZE - 1) #define FIFO_ENTRY_SIZE (1 << 31) #define FIFO_ENTRIES (voodoo->fifo_write_idx - voodoo->fifo_read_idx) #define FIFO_FULL ((voodoo->fifo_write_idx - voodoo->fifo_read_idx) >= FIFO_SIZE) #define FIFO_EMPTY (voodoo->fifo_read_idx == voodoo->fifo_write_idx) #define FIFO_TYPE 0xff000000 #define FIFO_ADDR 0x00ffffff enum { FIFO_INVALID = (0x00 << 24), FIFO_WRITEL_REG = (0x01 << 24), FIFO_WRITEW_FB = (0x02 << 24), FIFO_WRITEL_FB = (0x03 << 24), FIFO_WRITEL_TEX = (0x04 << 24) }; #define PARAM_SIZE 1024 #define PARAM_MASK (PARAM_SIZE - 1) #define PARAM_ENTRY_SIZE (1 << 31) #define PARAM_ENTRIES_1 (voodoo->params_write_idx - voodoo->params_read_idx[0]) #define PARAM_ENTRIES_2 (voodoo->params_write_idx - voodoo->params_read_idx[1]) #define PARAM_FULL_1 ((voodoo->params_write_idx - voodoo->params_read_idx[0]) >= PARAM_SIZE) #define PARAM_FULL_2 ((voodoo->params_write_idx - voodoo->params_read_idx[1]) >= PARAM_SIZE) #define PARAM_EMPTY_1 (voodoo->params_read_idx[0] == voodoo->params_write_idx) #define PARAM_EMPTY_2 (voodoo->params_read_idx[1] == voodoo->params_write_idx) typedef struct { uint32_t addr_type; uint32_t val; } fifo_entry_t; static rgba8_t rgb332[0x100], ai44[0x100], rgb565[0x10000], argb1555[0x10000], argb4444[0x10000], ai88[0x10000]; typedef struct voodoo_params_t { int command; int32_t vertexAx, vertexAy, vertexBx, vertexBy, vertexCx, vertexCy; uint32_t startR, startG, startB, startZ, startA; int32_t dBdX, dGdX, dRdX, dAdX, dZdX; int32_t dBdY, dGdY, dRdY, dAdY, dZdY; int64_t startW, dWdX, dWdY; struct { int64_t startS, startT, startW, p1; int64_t dSdX, dTdX, dWdX, p2; int64_t dSdY, dTdY, dWdY, p3; } tmu[2]; uint32_t color0, color1; uint32_t fbzMode; uint32_t fbzColorPath; uint32_t fogMode; rgb_t fogColor; struct { uint8_t fog, dfog; } fogTable[64]; uint32_t alphaMode; uint32_t zaColor; int chromaKey_r, chromaKey_g, chromaKey_b; uint32_t chromaKey; uint32_t textureMode[2]; uint32_t tLOD[2]; uint32_t texBaseAddr[2], texBaseAddr1[2], texBaseAddr2[2], texBaseAddr38[2]; uint32_t tex_base[2][LOD_MAX+2]; int tex_width[2]; int tex_w_mask[2][LOD_MAX+2]; int tex_w_nmask[2][LOD_MAX+2]; int tex_h_mask[2][LOD_MAX+2]; int tex_shift[2][LOD_MAX+2]; int tex_lod[2][LOD_MAX+2]; int tex_entry[2]; int detail_max[2], detail_bias[2], detail_scale[2]; uint32_t draw_offset, aux_offset; int tformat[2]; int clipLeft, clipRight, clipLowY, clipHighY; int sign; uint32_t front_offset; uint32_t swapbufferCMD; uint32_t stipple; } voodoo_params_t; typedef struct texture_t { uint32_t base; uint32_t tLOD; volatile int refcount, refcount_r[2]; int is16; uint32_t palette_checksum; uint32_t addr_start, addr_end; uint32_t *data; } texture_t; typedef struct voodoo_t { mem_mapping_t mapping; int pci_enable; uint8_t dac_data[8]; int dac_reg, dac_reg_ff; uint8_t dac_readdata; uint16_t dac_pll_regs[16]; float pixel_clock; int line_time; voodoo_params_t params; uint32_t fbiInit0, fbiInit1, fbiInit2, fbiInit3, fbiInit4; uint32_t fbiInit5, fbiInit6, fbiInit7; /*Voodoo 2*/ uint8_t initEnable; uint32_t lfbMode; uint32_t memBaseAddr; int_float fvertexAx, fvertexAy, fvertexBx, fvertexBy, fvertexCx, fvertexCy; uint32_t front_offset, back_offset; uint32_t fb_read_offset, fb_write_offset; int row_width; int block_width; uint8_t *fb_mem, *tex_mem[2]; uint16_t *tex_mem_w[2]; int rgb_sel; uint32_t trexInit1[2]; int swap_count; int disp_buffer, draw_buffer; int timer_count; int line; svga_t *svga; uint32_t backPorch; uint32_t videoDimensions; uint32_t hSync, vSync; int h_total, v_total, v_disp; int h_disp; int v_retrace; struct { uint32_t y[4], i[4], q[4]; } nccTable[2][2]; rgba_u palette[2][256]; rgba_u ncc_lookup[2][2][256]; int ncc_dirty[2]; thread_t *fifo_thread; thread_t *render_thread[2]; event_t *wake_fifo_thread; event_t *wake_main_thread; event_t *fifo_not_full_event; event_t *render_not_full_event[2]; event_t *wake_render_thread[2]; int voodoo_busy; int render_voodoo_busy[2]; int render_threads; int odd_even_mask; int pixel_count[2], texel_count[2], tri_count, frame_count; int pixel_count_old[2], texel_count_old[2]; int wr_count, rd_count, tex_count; int retrace_count; int swap_interval; uint32_t swap_offset; int swap_pending; int bilinear_enabled; int fb_size; uint32_t fb_mask; int texture_size; uint32_t texture_mask; int dual_tmus; int type; fifo_entry_t fifo[FIFO_SIZE]; volatile int fifo_read_idx, fifo_write_idx; int cmd_read, cmd_written; voodoo_params_t params_buffer[PARAM_SIZE]; volatile int params_read_idx[2], params_write_idx; uint32_t cmdfifo_base, cmdfifo_end; int cmdfifo_rp; volatile int cmdfifo_depth_rd, cmdfifo_depth_wr; uint32_t cmdfifo_amin, cmdfifo_amax; uint32_t sSetupMode; struct { float sVx, sVy; float sRed, sGreen, sBlue, sAlpha; float sVz, sWb; float sW0, sS0, sT0; float sW1, sS1, sT1; } verts[4]; int vertex_num; int num_verticies; int flush; int scrfilter; int scrfilterEnabled; int scrfilterThreshold; int scrfilterThresholdOld; uint32_t last_write_addr; uint32_t fbiPixelsIn; uint32_t fbiChromaFail; uint32_t fbiZFuncFail; uint32_t fbiAFuncFail; uint32_t fbiPixelsOut; uint32_t bltSrcBaseAddr; uint32_t bltDstBaseAddr; int bltSrcXYStride, bltDstXYStride; uint32_t bltSrcChromaRange, bltDstChromaRange; int bltSrcChromaMinR, bltSrcChromaMinG, bltSrcChromaMinB; int bltSrcChromaMaxR, bltSrcChromaMaxG, bltSrcChromaMaxB; int bltDstChromaMinR, bltDstChromaMinG, bltDstChromaMinB; int bltDstChromaMaxR, bltDstChromaMaxG, bltDstChromaMaxB; int bltClipRight, bltClipLeft; int bltClipHighY, bltClipLowY; int bltSrcX, bltSrcY; int bltDstX, bltDstY; int bltSizeX, bltSizeY; int bltRop[4]; uint16_t bltColorFg, bltColorBg; uint32_t bltCommand; struct { int dst_x, dst_y; int cur_x; int size_x, size_y; int x_dir, y_dir; int dst_stride; } blt; rgb_t clutData[33]; int clutData_dirty; rgb_t clutData256[256]; uint32_t video_16to32[0x10000]; uint8_t dirty_line[1024]; int dirty_line_low, dirty_line_high; int fb_write_buffer, fb_draw_buffer; int buffer_cutoff; int read_time, write_time, burst_time; int wake_timer; uint8_t thefilter[256][256]; // pixel filter, feeding from one or two uint8_t thefilterg[256][256]; // for green uint8_t thefilterb[256][256]; // for blue /* the voodoo adds purple lines for some reason */ uint16_t purpleline[256][3]; texture_t texture_cache[2][TEX_CACHE_MAX]; uint8_t texture_present[2][4096]; int texture_last_removed; uint32_t palette_checksum[2]; int palette_dirty[2]; int use_recompiler; void *codegen_data; } voodoo_t; static inline void wait_for_render_thread_idle(voodoo_t *voodoo); enum { SST_status = 0x000, SST_intrCtrl = 0x004, 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_fvertexAx = 0x088, SST_fvertexAy = 0x08c, SST_fvertexBx = 0x090, SST_fvertexBy = 0x094, SST_fvertexCx = 0x098, SST_fvertexCy = 0x09c, SST_fstartR = 0x00a0, SST_fstartG = 0x00a4, SST_fstartB = 0x00a8, SST_fstartZ = 0x00ac, SST_fstartA = 0x00b0, SST_fstartS = 0x00b4, SST_fstartT = 0x00b8, SST_fstartW = 0x00bc, SST_fdRdX = 0x00c0, SST_fdGdX = 0x00c4, SST_fdBdX = 0x00c8, SST_fdZdX = 0x00cc, SST_fdAdX = 0x00d0, SST_fdSdX = 0x00d4, SST_fdTdX = 0x00d8, SST_fdWdX = 0x00dc, SST_fdRdY = 0x00e0, SST_fdGdY = 0x00e4, SST_fdBdY = 0x00e8, SST_fdZdY = 0x00ec, SST_fdAdY = 0x00f0, SST_fdSdY = 0x00f4, SST_fdTdY = 0x00f8, SST_fdWdY = 0x00fc, SST_ftriangleCMD = 0x0100, SST_fbzColorPath = 0x104, SST_fogMode = 0x108, SST_alphaMode = 0x10c, SST_fbzMode = 0x110, SST_lfbMode = 0x114, SST_clipLeftRight = 0x118, SST_clipLowYHighY = 0x11c, SST_nopCMD = 0x120, SST_fastfillCMD = 0x124, SST_swapbufferCMD = 0x128, SST_fogColor = 0x12c, SST_zaColor = 0x130, SST_chromaKey = 0x134, SST_userIntrCMD = 0x13c, SST_stipple = 0x140, SST_color0 = 0x144, SST_color1 = 0x148, SST_fbiPixelsIn = 0x14c, SST_fbiChromaFail = 0x150, SST_fbiZFuncFail = 0x154, SST_fbiAFuncFail = 0x158, SST_fbiPixelsOut = 0x15c, SST_fogTable00 = 0x160, SST_fogTable01 = 0x164, SST_fogTable02 = 0x168, SST_fogTable03 = 0x16c, SST_fogTable04 = 0x170, SST_fogTable05 = 0x174, SST_fogTable06 = 0x178, SST_fogTable07 = 0x17c, SST_fogTable08 = 0x180, SST_fogTable09 = 0x184, SST_fogTable0a = 0x188, SST_fogTable0b = 0x18c, SST_fogTable0c = 0x190, SST_fogTable0d = 0x194, SST_fogTable0e = 0x198, SST_fogTable0f = 0x19c, SST_fogTable10 = 0x1a0, SST_fogTable11 = 0x1a4, SST_fogTable12 = 0x1a8, SST_fogTable13 = 0x1ac, SST_fogTable14 = 0x1b0, SST_fogTable15 = 0x1b4, SST_fogTable16 = 0x1b8, SST_fogTable17 = 0x1bc, SST_fogTable18 = 0x1c0, SST_fogTable19 = 0x1c4, SST_fogTable1a = 0x1c8, SST_fogTable1b = 0x1cc, SST_fogTable1c = 0x1d0, SST_fogTable1d = 0x1d4, SST_fogTable1e = 0x1d8, SST_fogTable1f = 0x1dc, SST_cmdFifoBaseAddr = 0x1e0, SST_cmdFifoBump = 0x1e4, SST_cmdFifoRdPtr = 0x1e8, SST_cmdFifoAMin = 0x1ec, SST_cmdFifoAMax = 0x1f0, SST_cmdFifoDepth = 0x1f4, SST_cmdFifoHoles = 0x1f8, SST_fbiInit4 = 0x200, SST_backPorch = 0x208, SST_videoDimensions = 0x20c, SST_fbiInit0 = 0x210, SST_fbiInit1 = 0x214, SST_fbiInit2 = 0x218, SST_fbiInit3 = 0x21c, SST_hSync = 0x220, SST_vSync = 0x224, SST_clutData = 0x228, SST_dacData = 0x22c, SST_scrFilter = 0x230, SST_hvRetrace = 0x240, SST_fbiInit5 = 0x244, SST_fbiInit6 = 0x248, SST_fbiInit7 = 0x24c, SST_sSetupMode = 0x260, SST_sVx = 0x264, SST_sVy = 0x268, SST_sARGB = 0x26c, SST_sRed = 0x270, SST_sGreen = 0x274, SST_sBlue = 0x278, SST_sAlpha = 0x27c, SST_sVz = 0x280, SST_sWb = 0x284, SST_sW0 = 0x288, SST_sS0 = 0x28c, SST_sT0 = 0x290, SST_sW1 = 0x294, SST_sS1 = 0x298, SST_sT1 = 0x29c, SST_sDrawTriCMD = 0x2a0, SST_sBeginTriCMD = 0x2a4, SST_bltSrcBaseAddr = 0x2c0, SST_bltDstBaseAddr = 0x2c4, SST_bltXYStrides = 0x2c8, SST_bltSrcChromaRange = 0x2cc, SST_bltDstChromaRange = 0x2d0, SST_bltClipX = 0x2d4, SST_bltClipY = 0x2d8, SST_bltSrcXY = 0x2e0, SST_bltDstXY = 0x2e4, SST_bltSize = 0x2e8, SST_bltRop = 0x2ec, SST_bltColor = 0x2f0, SST_bltCommand = 0x2f8, SST_bltData = 0x2fc, SST_textureMode = 0x300, SST_tLOD = 0x304, SST_tDetail = 0x308, SST_texBaseAddr = 0x30c, SST_texBaseAddr1 = 0x310, SST_texBaseAddr2 = 0x314, SST_texBaseAddr38 = 0x318, SST_trexInit1 = 0x320, SST_nccTable0_Y0 = 0x324, SST_nccTable0_Y1 = 0x328, SST_nccTable0_Y2 = 0x32c, SST_nccTable0_Y3 = 0x330, SST_nccTable0_I0 = 0x334, SST_nccTable0_I1 = 0x338, SST_nccTable0_I2 = 0x33c, SST_nccTable0_I3 = 0x340, SST_nccTable0_Q0 = 0x344, SST_nccTable0_Q1 = 0x348, SST_nccTable0_Q2 = 0x34c, SST_nccTable0_Q3 = 0x350, SST_nccTable1_Y0 = 0x354, SST_nccTable1_Y1 = 0x358, SST_nccTable1_Y2 = 0x35c, SST_nccTable1_Y3 = 0x360, SST_nccTable1_I0 = 0x364, SST_nccTable1_I1 = 0x368, SST_nccTable1_I2 = 0x36c, SST_nccTable1_I3 = 0x370, SST_nccTable1_Q0 = 0x374, SST_nccTable1_Q1 = 0x378, SST_nccTable1_Q2 = 0x37c, SST_nccTable1_Q3 = 0x380, SST_remap_status = 0x000 | 0x400, SST_remap_vertexAx = 0x008 | 0x400, SST_remap_vertexAy = 0x00c | 0x400, SST_remap_vertexBx = 0x010 | 0x400, SST_remap_vertexBy = 0x014 | 0x400, SST_remap_vertexCx = 0x018 | 0x400, SST_remap_vertexCy = 0x01c | 0x400, SST_remap_startR = 0x0020 | 0x400, SST_remap_startG = 0x002c | 0x400, SST_remap_startB = 0x0038 | 0x400, SST_remap_startZ = 0x0044 | 0x400, SST_remap_startA = 0x0050 | 0x400, SST_remap_startS = 0x005c | 0x400, SST_remap_startT = 0x0068 | 0x400, SST_remap_startW = 0x0074 | 0x400, SST_remap_dRdX = 0x0024 | 0x400, SST_remap_dGdX = 0x0030 | 0x400, SST_remap_dBdX = 0x003c | 0x400, SST_remap_dZdX = 0x0048 | 0x400, SST_remap_dAdX = 0x0054 | 0x400, SST_remap_dSdX = 0x0060 | 0x400, SST_remap_dTdX = 0x006c | 0x400, SST_remap_dWdX = 0x0078 | 0x400, SST_remap_dRdY = 0x0028 | 0x400, SST_remap_dGdY = 0x0034 | 0x400, SST_remap_dBdY = 0x0040 | 0x400, SST_remap_dZdY = 0x004c | 0x400, SST_remap_dAdY = 0x0058 | 0x400, SST_remap_dSdY = 0x0064 | 0x400, SST_remap_dTdY = 0x0070 | 0x400, SST_remap_dWdY = 0x007c | 0x400, SST_remap_triangleCMD = 0x0080 | 0x400, SST_remap_fvertexAx = 0x088 | 0x400, SST_remap_fvertexAy = 0x08c | 0x400, SST_remap_fvertexBx = 0x090 | 0x400, SST_remap_fvertexBy = 0x094 | 0x400, SST_remap_fvertexCx = 0x098 | 0x400, SST_remap_fvertexCy = 0x09c | 0x400, SST_remap_fstartR = 0x00a0 | 0x400, SST_remap_fstartG = 0x00ac | 0x400, SST_remap_fstartB = 0x00b8 | 0x400, SST_remap_fstartZ = 0x00c4 | 0x400, SST_remap_fstartA = 0x00d0 | 0x400, SST_remap_fstartS = 0x00dc | 0x400, SST_remap_fstartT = 0x00e8 | 0x400, SST_remap_fstartW = 0x00f4 | 0x400, SST_remap_fdRdX = 0x00a4 | 0x400, SST_remap_fdGdX = 0x00b0 | 0x400, SST_remap_fdBdX = 0x00bc | 0x400, SST_remap_fdZdX = 0x00c8 | 0x400, SST_remap_fdAdX = 0x00d4 | 0x400, SST_remap_fdSdX = 0x00e0 | 0x400, SST_remap_fdTdX = 0x00ec | 0x400, SST_remap_fdWdX = 0x00f8 | 0x400, SST_remap_fdRdY = 0x00a8 | 0x400, SST_remap_fdGdY = 0x00b4 | 0x400, SST_remap_fdBdY = 0x00c0 | 0x400, SST_remap_fdZdY = 0x00cc | 0x400, SST_remap_fdAdY = 0x00d8 | 0x400, SST_remap_fdSdY = 0x00e4 | 0x400, SST_remap_fdTdY = 0x00f0 | 0x400, SST_remap_fdWdY = 0x00fc | 0x400, }; 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_RGB555 = 1, LFB_FORMAT_ARGB1555 = 2, LFB_FORMAT_ARGB8888 = 5, LFB_FORMAT_DEPTH = 15, LFB_FORMAT_MASK = 15 }; enum { LFB_WRITE_COLOUR = 1, LFB_WRITE_DEPTH = 2 }; enum { FBZ_CHROMAKEY = (1 << 1), FBZ_W_BUFFER = (1 << 3), FBZ_DEPTH_ENABLE = (1 << 4), FBZ_DITHER = (1 << 8), FBZ_RGB_WMASK = (1 << 9), FBZ_DEPTH_WMASK = (1 << 10), FBZ_DITHER_2x2 = (1 << 11), FBZ_DRAW_FRONT = 0x0000, FBZ_DRAW_BACK = 0x4000, FBZ_DRAW_MASK = 0xc000, FBZ_DEPTH_BIAS = (1 << 16), FBZ_DEPTH_SOURCE = (1 << 20), FBZ_PARAM_ADJUST = (1 << 26) }; enum { TEX_RGB332 = 0x0, TEX_Y4I2Q2 = 0x1, TEX_A8 = 0x2, TEX_I8 = 0x3, TEX_AI8 = 0x4, TEX_PAL8 = 0x5, TEX_APAL8 = 0x6, TEX_ARGB8332 = 0x8, TEX_A8Y4I2Q2 = 0x9, TEX_R5G6B5 = 0xa, TEX_ARGB1555 = 0xb, TEX_ARGB4444 = 0xc, TEX_A8I8 = 0xd, TEX_APAL88 = 0xe }; enum { TEXTUREMODE_NCC_SEL = (1 << 5), TEXTUREMODE_TCLAMPS = (1 << 6), TEXTUREMODE_TCLAMPT = (1 << 7), TEXTUREMODE_TRILINEAR = (1 << 30) }; enum { FBIINIT0_VGA_PASS = 1, FBIINIT0_GRAPHICS_RESET = (1 << 1) }; enum { FBIINIT3_REMAP = 1 }; enum { FBIINIT7_CMDFIFO_ENABLE = (1 << 8) }; enum { CC_LOCALSELECT_ITER_RGB = 0, CC_LOCALSELECT_TEX = 1, CC_LOCALSELECT_COLOR1 = 2, CC_LOCALSELECT_LFB = 3 }; enum { CCA_LOCALSELECT_ITER_A = 0, CCA_LOCALSELECT_COLOR0 = 1, CCA_LOCALSELECT_ITER_Z = 2 }; enum { C_SEL_ITER_RGB = 0, C_SEL_TEX = 1, C_SEL_COLOR1 = 2, C_SEL_LFB = 3 }; enum { A_SEL_ITER_A = 0, A_SEL_TEX = 1, A_SEL_COLOR1 = 2, A_SEL_LFB = 3 }; enum { CC_MSELECT_ZERO = 0, CC_MSELECT_CLOCAL = 1, CC_MSELECT_AOTHER = 2, CC_MSELECT_ALOCAL = 3, CC_MSELECT_TEX = 4, CC_MSELECT_TEXRGB = 5 }; enum { CCA_MSELECT_ZERO = 0, CCA_MSELECT_ALOCAL = 1, CCA_MSELECT_AOTHER = 2, CCA_MSELECT_ALOCAL2 = 3, CCA_MSELECT_TEX = 4 }; enum { TC_MSELECT_ZERO = 0, TC_MSELECT_CLOCAL = 1, TC_MSELECT_AOTHER = 2, TC_MSELECT_ALOCAL = 3, TC_MSELECT_DETAIL = 4, TC_MSELECT_LOD_FRAC = 5 }; enum { TCA_MSELECT_ZERO = 0, TCA_MSELECT_CLOCAL = 1, TCA_MSELECT_AOTHER = 2, TCA_MSELECT_ALOCAL = 3, TCA_MSELECT_DETAIL = 4, TCA_MSELECT_LOD_FRAC = 5 }; enum { CC_ADD_CLOCAL = 1, CC_ADD_ALOCAL = 2 }; enum { CCA_ADD_CLOCAL = 1, CCA_ADD_ALOCAL = 2 }; enum { AFUNC_AZERO = 0x0, AFUNC_ASRC_ALPHA = 0x1, AFUNC_A_COLOR = 0x2, AFUNC_ADST_ALPHA = 0x3, AFUNC_AONE = 0x4, AFUNC_AOMSRC_ALPHA = 0x5, AFUNC_AOM_COLOR = 0x6, AFUNC_AOMDST_ALPHA = 0x7, AFUNC_ASATURATE = 0xf }; enum { AFUNC_ACOLORBEFOREFOG = 0xf }; enum { AFUNC_NEVER = 0, AFUNC_LESSTHAN = 1, AFUNC_EQUAL = 2, AFUNC_LESSTHANEQUAL = 3, AFUNC_GREATERTHAN = 4, AFUNC_NOTEQUAL = 5, AFUNC_GREATERTHANEQUAL = 6, AFUNC_ALWAYS = 7 }; enum { DEPTHOP_NEVER = 0, DEPTHOP_LESSTHAN = 1, DEPTHOP_EQUAL = 2, DEPTHOP_LESSTHANEQUAL = 3, DEPTHOP_GREATERTHAN = 4, DEPTHOP_NOTEQUAL = 5, DEPTHOP_GREATERTHANEQUAL = 6, DEPTHOP_ALWAYS = 7 }; enum { FOG_ENABLE = 0x01, FOG_ADD = 0x02, FOG_MULT = 0x04, FOG_ALPHA = 0x08, FOG_Z = 0x10, FOG_W = 0x18, FOG_CONSTANT = 0x20 }; enum { LOD_ODD = (1 << 18), LOD_SPLIT = (1 << 19), LOD_S_IS_WIDER = (1 << 20), LOD_TMIRROR_S = (1 << 28), LOD_TMIRROR_T = (1 << 29) }; enum { CMD_INVALID = 0, CMD_DRAWTRIANGLE, CMD_FASTFILL, CMD_SWAPBUF }; enum { FBZCP_TEXTURE_ENABLED = (1 << 27) }; enum { BLTCMD_SRC_TILED = (1 << 14), BLTCMD_DST_TILED = (1 << 15) }; #define TEXTUREMODE_MASK 0x3ffff000 #define TEXTUREMODE_PASSTHROUGH 0 #define TEXTUREMODE_LOCAL_MASK 0x00643000 #define TEXTUREMODE_LOCAL 0x00241000 static void voodoo_threshold_check(voodoo_t *voodoo); static void voodoo_update_ncc(voodoo_t *voodoo, int tmu) { int tbl; for (tbl = 0; tbl < 2; tbl++) { int col; for (col = 0; col < 256; col++) { int y = (col >> 4), i = (col >> 2) & 3, q = col & 3; int _y = (col >> 4), _i = (col >> 2) & 3, _q = col & 3; int i_r, i_g, i_b; int q_r, q_g, q_b; int r, g, b; y = (voodoo->nccTable[tmu][tbl].y[y >> 2] >> ((y & 3) * 8)) & 0xff; i_r = (voodoo->nccTable[tmu][tbl].i[i] >> 18) & 0x1ff; if (i_r & 0x100) i_r |= 0xfffffe00; i_g = (voodoo->nccTable[tmu][tbl].i[i] >> 9) & 0x1ff; if (i_g & 0x100) i_g |= 0xfffffe00; i_b = voodoo->nccTable[tmu][tbl].i[i] & 0x1ff; if (i_b & 0x100) i_b |= 0xfffffe00; q_r = (voodoo->nccTable[tmu][tbl].q[q] >> 18) & 0x1ff; if (q_r & 0x100) q_r |= 0xfffffe00; q_g = (voodoo->nccTable[tmu][tbl].q[q] >> 9) & 0x1ff; if (q_g & 0x100) q_g |= 0xfffffe00; q_b = voodoo->nccTable[tmu][tbl].q[q] & 0x1ff; if (q_b & 0x100) q_b |= 0xfffffe00; voodoo->ncc_lookup[tmu][tbl][col].rgba.r = CLAMP(y + i_r + q_r); voodoo->ncc_lookup[tmu][tbl][col].rgba.g = CLAMP(y + i_g + q_g); voodoo->ncc_lookup[tmu][tbl][col].rgba.b = CLAMP(y + i_b + q_b); voodoo->ncc_lookup[tmu][tbl][col].rgba.a = 0xff; } } } #define TRIPLE_BUFFER ((voodoo->fbiInit2 & 0x10) || (voodoo->fbiInit5 & 0x600) == 0x400) static void voodoo_recalc(voodoo_t *voodoo) { uint32_t buffer_offset = ((voodoo->fbiInit2 >> 11) & 511) * 4096; 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; /* pclog("voodoo_recalc : front_offset %08X back_offset %08X aux_offset %08X draw_offset %08x\n", voodoo->params.front_offset, voodoo->back_offset, voodoo->params.aux_offset, voodoo->params.draw_offset); pclog(" fb_read_offset %08X fb_write_offset %08X row_width %i %08x %08x\n", voodoo->fb_read_offset, voodoo->fb_write_offset, voodoo->row_width, voodoo->lfbMode, voodoo->params.fbzMode);*/ } static void voodoo_recalc_tex(voodoo_t *voodoo, int tmu) { int aspect = (voodoo->params.tLOD[tmu] >> 21) & 3; int width = 256, height = 256; int shift = 8; int lod; int lod_min = (voodoo->params.tLOD[tmu] >> 2) & 15; int lod_max = (voodoo->params.tLOD[tmu] >> 8) & 15; uint32_t base = voodoo->params.texBaseAddr[tmu]; int tex_lod = 0; if (voodoo->params.tLOD[tmu] & LOD_S_IS_WIDER) height >>= aspect; else { width >>= aspect; shift -= aspect; } if ((voodoo->params.tLOD[tmu] & LOD_SPLIT) && (voodoo->params.tLOD[tmu] & LOD_ODD)) { width >>= 1; height >>= 1; shift--; tex_lod++; } for (lod = 0; lod <= LOD_MAX+1; lod++) { if (!width) width = 1; if (!height) height = 1; if (shift < 0) shift = 0; voodoo->params.tex_base[tmu][lod] = base; voodoo->params.tex_w_mask[tmu][lod] = width - 1; voodoo->params.tex_w_nmask[tmu][lod] = ~(width - 1); voodoo->params.tex_h_mask[tmu][lod] = height - 1; voodoo->params.tex_shift[tmu][lod] = shift; voodoo->params.tex_lod[tmu][lod] = tex_lod; if (!(voodoo->params.tLOD[tmu] & LOD_SPLIT) || ((lod & 1) && (voodoo->params.tLOD[tmu] & LOD_ODD)) || (!(lod & 1) && !(voodoo->params.tLOD[tmu] & LOD_ODD))) { if (!(voodoo->params.tLOD[tmu] & LOD_ODD) || lod != 0) { if (voodoo->params.tformat[tmu] & 8) base += width * height * 2; else base += width * height; if (voodoo->params.tLOD[tmu] & LOD_SPLIT) { width >>= 2; height >>= 2; shift -= 2; tex_lod += 2; } else { width >>= 1; height >>= 1; shift--; tex_lod++; } } } } voodoo->params.tex_width[tmu] = width; } #define makergba(r, g, b, a) ((b) | ((g) << 8) | ((r) << 16) | ((a) << 24)) static void use_texture(voodoo_t *voodoo, voodoo_params_t *params, int tmu) { int c; int lod; int lod_min, lod_max; uint32_t addr = 0, addr_end; uint32_t palette_checksum; lod_min = (params->tLOD[tmu] >> 2) & 15; lod_max = (params->tLOD[tmu] >> 8) & 15; if (params->tformat[tmu] == TEX_PAL8 || params->tformat[tmu] == TEX_APAL8 || params->tformat[tmu] == TEX_APAL88) { if (voodoo->palette_dirty[tmu]) { palette_checksum = 0; for (c = 0; c < 256; c++) palette_checksum ^= voodoo->palette[tmu][c].u; voodoo->palette_checksum[tmu] = palette_checksum; voodoo->palette_dirty[tmu] = 0; } else palette_checksum = voodoo->palette_checksum[tmu]; } else palette_checksum = 0; /*Try to find texture in cache*/ for (c = 0; c < TEX_CACHE_MAX; c++) { if (voodoo->texture_cache[tmu][c].base == params->texBaseAddr[tmu] && voodoo->texture_cache[tmu][c].tLOD == (params->tLOD[tmu] & 0xf00fff) && voodoo->texture_cache[tmu][c].palette_checksum == palette_checksum) { params->tex_entry[tmu] = c; voodoo->texture_cache[tmu][c].refcount++; return; } } /*Texture not found, search for unused texture*/ do { for (c = 0; c < TEX_CACHE_MAX; c++) { voodoo->texture_last_removed++; voodoo->texture_last_removed &= (TEX_CACHE_MAX-1); if (voodoo->texture_cache[tmu][voodoo->texture_last_removed].refcount == voodoo->texture_cache[tmu][voodoo->texture_last_removed].refcount_r[0] && (voodoo->render_threads == 1 || voodoo->texture_cache[tmu][voodoo->texture_last_removed].refcount == voodoo->texture_cache[tmu][voodoo->texture_last_removed].refcount_r[1])) break; } if (c == TEX_CACHE_MAX) wait_for_render_thread_idle(voodoo); } while (c == TEX_CACHE_MAX); if (c == TEX_CACHE_MAX) fatal("Texture cache full!\n"); c = voodoo->texture_last_removed; voodoo->texture_cache[tmu][c].base = params->texBaseAddr[tmu]; voodoo->texture_cache[tmu][c].tLOD = params->tLOD[tmu] & 0xf00fff; lod_min = (params->tLOD[tmu] >> 2) & 15; lod_max = (params->tLOD[tmu] >> 8) & 15; // pclog(" add new texture to %i tformat=%i %08x LOD=%i-%i\n", c, voodoo->params.tformat[tmu], params->texBaseAddr[tmu], lod_min, lod_max); for (lod = lod_min; lod <= lod_max; lod++) { uint32_t *base = &voodoo->texture_cache[tmu][c].data[texture_offset[lod]]; uint32_t tex_addr = params->tex_base[tmu][lod] & voodoo->texture_mask; int x, y; int shift = 8 - params->tex_lod[tmu][lod]; rgba_u *pal; //pclog(" LOD %i : %08x - %08x %i %i,%i\n", lod, params->tex_base[tmu][lod] & voodoo->texture_mask, addr, voodoo->params.tformat[tmu], voodoo->params.tex_w_mask[tmu][lod],voodoo->params.tex_h_mask[tmu][lod]); switch (params->tformat[tmu]) { case TEX_RGB332: for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint8_t dat = voodoo->tex_mem[tmu][(tex_addr+x) & voodoo->texture_mask]; base[x] = makergba(rgb332[dat].r, rgb332[dat].g, rgb332[dat].b, 0xff); } tex_addr += (1 << voodoo->params.tex_shift[tmu][lod]); base += (1 << shift); } break; case TEX_Y4I2Q2: pal = voodoo->ncc_lookup[tmu][(voodoo->params.textureMode[tmu] & TEXTUREMODE_NCC_SEL) ? 1 : 0]; for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint8_t dat = voodoo->tex_mem[tmu][(tex_addr+x) & voodoo->texture_mask]; base[x] = makergba(pal[dat].rgba.r, pal[dat].rgba.g, pal[dat].rgba.b, 0xff); } tex_addr += (1 << voodoo->params.tex_shift[tmu][lod]); base += (1 << shift); } break; case TEX_A8: for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint8_t dat = voodoo->tex_mem[tmu][(tex_addr+x) & voodoo->texture_mask]; base[x] = makergba(dat, dat, dat, dat); } tex_addr += (1 << voodoo->params.tex_shift[tmu][lod]); base += (1 << shift); } break; case TEX_I8: for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint8_t dat = voodoo->tex_mem[tmu][(tex_addr+x) & voodoo->texture_mask]; base[x] = makergba(dat, dat, dat, 0xff); } tex_addr += (1 << voodoo->params.tex_shift[tmu][lod]); base += (1 << shift); } break; case TEX_AI8: for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint8_t dat = voodoo->tex_mem[tmu][(tex_addr+x) & voodoo->texture_mask]; base[x] = makergba((dat & 0x0f) | ((dat << 4) & 0xf0), (dat & 0x0f) | ((dat << 4) & 0xf0), (dat & 0x0f) | ((dat << 4) & 0xf0), (dat & 0xf0) | ((dat >> 4) & 0x0f)); } tex_addr += (1 << voodoo->params.tex_shift[tmu][lod]); base += (1 << shift); } break; case TEX_PAL8: pal = voodoo->palette[tmu]; for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint8_t dat = voodoo->tex_mem[tmu][(tex_addr+x) & voodoo->texture_mask]; base[x] = makergba(pal[dat].rgba.r, pal[dat].rgba.g, pal[dat].rgba.b, 0xff); } tex_addr += (1 << voodoo->params.tex_shift[tmu][lod]); base += (1 << shift); } break; case TEX_APAL8: pal = voodoo->palette[tmu]; for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint8_t dat = voodoo->tex_mem[tmu][(tex_addr+x) & voodoo->texture_mask]; int r = ((pal[dat].rgba.r & 3) << 6) | ((pal[dat].rgba.g & 0xf0) >> 2) | (pal[dat].rgba.r & 3); int g = ((pal[dat].rgba.g & 0xf) << 4) | ((pal[dat].rgba.b & 0xc0) >> 4) | ((pal[dat].rgba.g & 0xf) >> 2); int b = ((pal[dat].rgba.b & 0x3f) << 2) | ((pal[dat].rgba.b & 0x30) >> 4); int a = (pal[dat].rgba.r & 0xfc) | ((pal[dat].rgba.r & 0xc0) >> 6); base[x] = makergba(r, g, b, a); } tex_addr += (1 << voodoo->params.tex_shift[tmu][lod]); base += (1 << shift); } break; case TEX_ARGB8332: for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint16_t dat = *(uint16_t *)&voodoo->tex_mem[tmu][(tex_addr + x*2) & voodoo->texture_mask]; base[x] = makergba(rgb332[dat & 0xff].r, rgb332[dat & 0xff].g, rgb332[dat & 0xff].b, dat >> 8); } tex_addr += (1 << (voodoo->params.tex_shift[tmu][lod]+1)); base += (1 << shift); } break; case TEX_A8Y4I2Q2: pal = voodoo->ncc_lookup[tmu][(voodoo->params.textureMode[tmu] & TEXTUREMODE_NCC_SEL) ? 1 : 0]; for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint16_t dat = *(uint16_t *)&voodoo->tex_mem[tmu][(tex_addr + x*2) & voodoo->texture_mask]; base[x] = makergba(pal[dat & 0xff].rgba.r, pal[dat & 0xff].rgba.g, pal[dat & 0xff].rgba.b, dat >> 8); } tex_addr += (1 << (voodoo->params.tex_shift[tmu][lod]+1)); base += (1 << shift); } break; case TEX_R5G6B5: for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint16_t dat = *(uint16_t *)&voodoo->tex_mem[tmu][(tex_addr + x*2) & voodoo->texture_mask]; base[x] = makergba(rgb565[dat].r, rgb565[dat].g, rgb565[dat].b, 0xff); } tex_addr += (1 << (voodoo->params.tex_shift[tmu][lod]+1)); base += (1 << shift); } break; case TEX_ARGB1555: for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint16_t dat = *(uint16_t *)&voodoo->tex_mem[tmu][(tex_addr + x*2) & voodoo->texture_mask]; base[x] = makergba(argb1555[dat].r, argb1555[dat].g, argb1555[dat].b, argb1555[dat].a); } tex_addr += (1 << (voodoo->params.tex_shift[tmu][lod]+1)); base += (1 << shift); } break; case TEX_ARGB4444: for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint16_t dat = *(uint16_t *)&voodoo->tex_mem[tmu][(tex_addr + x*2) & voodoo->texture_mask]; base[x] = makergba(argb4444[dat].r, argb4444[dat].g, argb4444[dat].b, argb4444[dat].a); } tex_addr += (1 << (voodoo->params.tex_shift[tmu][lod]+1)); base += (1 << shift); } break; case TEX_A8I8: for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint16_t dat = *(uint16_t *)&voodoo->tex_mem[tmu][(tex_addr + x*2) & voodoo->texture_mask]; base[x] = makergba(dat & 0xff, dat & 0xff, dat & 0xff, dat >> 8); } tex_addr += (1 << (voodoo->params.tex_shift[tmu][lod]+1)); base += (1 << shift); } break; case TEX_APAL88: pal = voodoo->palette[tmu]; for (y = 0; y < voodoo->params.tex_h_mask[tmu][lod]+1; y++) { for (x = 0; x < voodoo->params.tex_w_mask[tmu][lod]+1; x++) { uint16_t dat = *(uint16_t *)&voodoo->tex_mem[tmu][(tex_addr + x*2) & voodoo->texture_mask]; base[x] = makergba(pal[dat & 0xff].rgba.r, pal[dat & 0xff].rgba.g, pal[dat & 0xff].rgba.b, dat >> 8); } tex_addr += (1 << (voodoo->params.tex_shift[tmu][lod]+1)); base += (1 << shift); } break; default: fatal("Unknown texture format %i\n", params->tformat[tmu]); } } voodoo->texture_cache[tmu][c].is16 = voodoo->params.tformat[tmu] & 8; if (params->tformat[tmu] == TEX_PAL8 || params->tformat[tmu] == TEX_APAL88) voodoo->texture_cache[tmu][c].palette_checksum = palette_checksum; else voodoo->texture_cache[tmu][c].palette_checksum = 0; addr = voodoo->params.tex_base[tmu][lod_min]; addr_end = voodoo->params.tex_base[tmu][lod_max+1]; voodoo->texture_cache[tmu][c].addr_start = addr; voodoo->texture_cache[tmu][c].addr_end = addr_end; for (; addr <= addr_end; addr += (1 << TEX_DIRTY_SHIFT)) voodoo->texture_present[tmu][(addr & voodoo->texture_mask) >> TEX_DIRTY_SHIFT] = 1; params->tex_entry[tmu] = c; voodoo->texture_cache[tmu][c].refcount++; } static void flush_texture_cache(voodoo_t *voodoo, uint32_t dirty_addr, int tmu) { int wait_for_idle = 0; int c; memset(voodoo->texture_present[tmu], 0, sizeof(voodoo->texture_present[0])); // pclog("Evict %08x %i\n", dirty_addr, sizeof(voodoo->texture_present)); for (c = 0; c < TEX_CACHE_MAX; c++) { if (voodoo->texture_cache[tmu][c].base != -1) { int lod_min = (voodoo->texture_cache[tmu][c].tLOD >> 2) & 15; int lod_max = (voodoo->texture_cache[tmu][c].tLOD >> 8) & 15; int addr_start = voodoo->texture_cache[tmu][c].addr_start; int addr_end = voodoo->texture_cache[tmu][c].addr_end; if (dirty_addr >= (addr_start & voodoo->texture_mask & ~0x3ff) && dirty_addr < (((addr_end & voodoo->texture_mask) + 0x3ff) & ~0x3ff)) { // pclog(" Evict texture %i %08x\n", c, voodoo->texture_cache[tmu][c].base); if (voodoo->texture_cache[tmu][c].refcount != voodoo->texture_cache[tmu][c].refcount_r[0] || (voodoo->render_threads == 2 && voodoo->texture_cache[tmu][c].refcount != voodoo->texture_cache[tmu][c].refcount_r[1])) wait_for_idle = 1; voodoo->texture_cache[tmu][c].base = -1; } else { for (; addr_start <= addr_end; addr_start += (1 << TEX_DIRTY_SHIFT)) voodoo->texture_present[tmu][(addr_start & voodoo->texture_mask) >> TEX_DIRTY_SHIFT] = 1; } } } if (wait_for_idle) wait_for_render_thread_idle(voodoo); } typedef struct voodoo_state_t { int xstart, xend, xdir; uint32_t base_r, base_g, base_b, base_a, base_z; struct { int64_t base_s, base_t, base_w; int lod; } tmu[2]; int64_t base_w; int lod; int lod_min[2], lod_max[2]; int dx1, dx2; int y, yend, ydir; int32_t dxAB, dxAC, dxBC; int tex_b[2], tex_g[2], tex_r[2], tex_a[2]; int tex_s, tex_t; int clamp_s[2], clamp_t[2]; int32_t vertexAx, vertexAy, vertexBx, vertexBy, vertexCx, vertexCy; uint32_t *tex[2][LOD_MAX+1]; int tformat; int *tex_w_mask[2]; int *tex_h_mask[2]; int *tex_shift[2]; int *tex_lod[2]; uint16_t *fb_mem, *aux_mem; int32_t ib, ig, ir, ia; int32_t z; int32_t new_depth; int64_t tmu0_s, tmu0_t; int64_t tmu0_w; int64_t tmu1_s, tmu1_t; int64_t tmu1_w; int64_t w; int pixel_count, texel_count; int x, x2; uint32_t w_depth; float log_temp; uint32_t ebp_store; uint32_t texBaseAddr; int lod_frac[2]; } voodoo_state_t; static int voodoo_output = 0; static uint8_t logtable[256] = { 0x00,0x01,0x02,0x04,0x05,0x07,0x08,0x09,0x0b,0x0c,0x0e,0x0f,0x10,0x12,0x13,0x15, 0x16,0x17,0x19,0x1a,0x1b,0x1d,0x1e,0x1f,0x21,0x22,0x23,0x25,0x26,0x27,0x28,0x2a, 0x2b,0x2c,0x2e,0x2f,0x30,0x31,0x33,0x34,0x35,0x36,0x38,0x39,0x3a,0x3b,0x3d,0x3e, 0x3f,0x40,0x41,0x43,0x44,0x45,0x46,0x47,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x50,0x51, 0x52,0x53,0x54,0x55,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74, 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x83,0x84,0x85, 0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8c,0x8d,0x8e,0x8f,0x90,0x91,0x92,0x93,0x94, 0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,0xa0,0xa1,0xa2,0xa2,0xa3, 0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xad,0xae,0xaf,0xb0,0xb1,0xb2, 0xb3,0xb4,0xb5,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbc,0xbd,0xbe,0xbf,0xc0, 0xc1,0xc2,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xcd, 0xce,0xcf,0xd0,0xd1,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd6,0xd7,0xd8,0xd9,0xda,0xda, 0xdb,0xdc,0xdd,0xde,0xde,0xdf,0xe0,0xe1,0xe1,0xe2,0xe3,0xe4,0xe5,0xe5,0xe6,0xe7, 0xe8,0xe8,0xe9,0xea,0xeb,0xeb,0xec,0xed,0xee,0xef,0xef,0xf0,0xf1,0xf2,0xf2,0xf3, 0xf4,0xf5,0xf5,0xf6,0xf7,0xf7,0xf8,0xf9,0xfa,0xfa,0xfb,0xfc,0xfd,0xfd,0xfe,0xff }; static inline int fastlog(uint64_t val) { uint64_t oldval = val; int exp = 63; int frac; if (!val || val & (1ULL << 63)) return 0x80000000; if (!(val & 0xffffffff00000000)) { exp -= 32; val <<= 32; } if (!(val & 0xffff000000000000)) { exp -= 16; val <<= 16; } if (!(val & 0xff00000000000000)) { exp -= 8; val <<= 8; } if (!(val & 0xf000000000000000)) { exp -= 4; val <<= 4; } if (!(val & 0xc000000000000000)) { exp -= 2; val <<= 2; } if (!(val & 0x8000000000000000)) { exp -= 1; val <<= 1; } if (exp >= 8) frac = (oldval >> (exp - 8)) & 0xff; else frac = (oldval << (8 - exp)) & 0xff; return (exp << 8) | logtable[frac]; } static inline int fls(uint16_t val) { int num = 0; //pclog("fls(%04x) = ", val); if (!(val & 0xff00)) { num += 8; val <<= 8; } if (!(val & 0xf000)) { num += 4; val <<= 4; } if (!(val & 0xc000)) { num += 2; val <<= 2; } if (!(val & 0x8000)) { num += 1; val <<= 1; } //pclog("%i %04x\n", num, val); return num; } typedef struct voodoo_texture_state_t { int s, t; int w_mask, h_mask; int tex_shift; } voodoo_texture_state_t; static inline void tex_read(voodoo_state_t *state, voodoo_texture_state_t *texture_state, int tmu) { uint32_t dat; if (texture_state->s & ~texture_state->w_mask) { if (state->clamp_s[tmu]) { if (texture_state->s < 0) texture_state->s = 0; if (texture_state->s > texture_state->w_mask) texture_state->s = texture_state->w_mask; } else texture_state->s &= texture_state->w_mask; } if (texture_state->t & ~texture_state->h_mask) { if (state->clamp_t[tmu]) { if (texture_state->t < 0) texture_state->t = 0; if (texture_state->t > texture_state->h_mask) texture_state->t = texture_state->h_mask; } else texture_state->t &= texture_state->h_mask; } dat = state->tex[tmu][state->lod][texture_state->s + (texture_state->t << texture_state->tex_shift)]; state->tex_b[tmu] = dat & 0xff; state->tex_g[tmu] = (dat >> 8) & 0xff; state->tex_r[tmu] = (dat >> 16) & 0xff; state->tex_a[tmu] = (dat >> 24) & 0xff; } #define LOW4(x) ((x & 0x0f) | ((x & 0x0f) << 4)) #define HIGH4(x) ((x & 0xf0) | ((x & 0xf0) >> 4)) static inline void tex_read_4(voodoo_state_t *state, voodoo_texture_state_t *texture_state, int s, int t, int *d, int tmu, int x) { rgba_u dat[4]; if (((s | (s + 1)) & ~texture_state->w_mask) || ((t | (t + 1)) & ~texture_state->h_mask)) { int c; for (c = 0; c < 4; c++) { int _s = s + (c & 1); int _t = t + ((c & 2) >> 1); if (_s & ~texture_state->w_mask) { if (state->clamp_s[tmu]) { if (_s < 0) _s = 0; if (_s > texture_state->w_mask) _s = texture_state->w_mask; } else _s &= texture_state->w_mask; } if (_t & ~texture_state->h_mask) { if (state->clamp_t[tmu]) { if (_t < 0) _t = 0; if (_t > texture_state->h_mask) _t = texture_state->h_mask; } else _t &= texture_state->h_mask; } dat[c].u = state->tex[tmu][state->lod][_s + (_t << texture_state->tex_shift)]; } } else { dat[0].u = state->tex[tmu][state->lod][s + (t << texture_state->tex_shift)]; dat[1].u = state->tex[tmu][state->lod][s + 1 + (t << texture_state->tex_shift)]; dat[2].u = state->tex[tmu][state->lod][s + ((t + 1) << texture_state->tex_shift)]; dat[3].u = state->tex[tmu][state->lod][s + 1 + ((t + 1) << texture_state->tex_shift)]; } state->tex_r[tmu] = (dat[0].rgba.r * d[0] + dat[1].rgba.r * d[1] + dat[2].rgba.r * d[2] + dat[3].rgba.r * d[3]) >> 8; state->tex_g[tmu] = (dat[0].rgba.g * d[0] + dat[1].rgba.g * d[1] + dat[2].rgba.g * d[2] + dat[3].rgba.g * d[3]) >> 8; state->tex_b[tmu] = (dat[0].rgba.b * d[0] + dat[1].rgba.b * d[1] + dat[2].rgba.b * d[2] + dat[3].rgba.b * d[3]) >> 8; state->tex_a[tmu] = (dat[0].rgba.a * d[0] + dat[1].rgba.a * d[1] + dat[2].rgba.a * d[2] + dat[3].rgba.a * d[3]) >> 8; } static inline void voodoo_get_texture(voodoo_t *voodoo, voodoo_params_t *params, voodoo_state_t *state, int tmu, int x) { rgba_u tex_samples[4]; voodoo_texture_state_t texture_state; int d[4]; int s, t; int tex_lod = state->tex_lod[tmu][state->lod]; texture_state.w_mask = state->tex_w_mask[tmu][state->lod]; texture_state.h_mask = state->tex_h_mask[tmu][state->lod]; texture_state.tex_shift = 8 - tex_lod; if (params->tLOD[tmu] & LOD_TMIRROR_S) { if (state->tex_s & 0x1000) state->tex_s = ~state->tex_s; } if (params->tLOD[tmu] & LOD_TMIRROR_T) { if (state->tex_t & 0x1000) state->tex_t = ~state->tex_t; } if (voodoo->bilinear_enabled && params->textureMode[tmu] & 6) { int _ds, dt; state->tex_s -= 1 << (3+tex_lod); state->tex_t -= 1 << (3+tex_lod); s = state->tex_s >> tex_lod; t = state->tex_t >> tex_lod; _ds = s & 0xf; dt = t & 0xf; s >>= 4; t >>= 4; //if (x == 80) //if (voodoo_output) // pclog("s=%08x t=%08x _ds=%02x _dt=%02x\n", s, t, _ds, dt); d[0] = (16 - _ds) * (16 - dt); d[1] = _ds * (16 - dt); d[2] = (16 - _ds) * dt; d[3] = _ds * dt; // texture_state.s = s; // texture_state.t = t; tex_read_4(state, &texture_state, s, t, d, tmu, x); /* state->tex_r = (tex_samples[0].rgba.r * d[0] + tex_samples[1].rgba.r * d[1] + tex_samples[2].rgba.r * d[2] + tex_samples[3].rgba.r * d[3]) >> 8; state->tex_g = (tex_samples[0].rgba.g * d[0] + tex_samples[1].rgba.g * d[1] + tex_samples[2].rgba.g * d[2] + tex_samples[3].rgba.g * d[3]) >> 8; state->tex_b = (tex_samples[0].rgba.b * d[0] + tex_samples[1].rgba.b * d[1] + tex_samples[2].rgba.b * d[2] + tex_samples[3].rgba.b * d[3]) >> 8; state->tex_a = (tex_samples[0].rgba.a * d[0] + tex_samples[1].rgba.a * d[1] + tex_samples[2].rgba.a * d[2] + tex_samples[3].rgba.a * d[3]) >> 8;*/ /* state->tex_r = tex_samples[0].r; state->tex_g = tex_samples[0].g; state->tex_b = tex_samples[0].b; state->tex_a = tex_samples[0].a;*/ } else { // rgba_t tex_samples; // voodoo_texture_state_t texture_state; // int s = state->tex_s >> (18+state->lod); // int t = state->tex_t >> (18+state->lod); // int s, t; // state->tex_s -= 1 << (17+state->lod); // state->tex_t -= 1 << (17+state->lod); s = state->tex_s >> (4+tex_lod); t = state->tex_t >> (4+tex_lod); texture_state.s = s; texture_state.t = t; tex_read(state, &texture_state, tmu); /* state->tex_r = tex_samples[0].rgba.r; state->tex_g = tex_samples[0].rgba.g; state->tex_b = tex_samples[0].rgba.b; state->tex_a = tex_samples[0].rgba.a;*/ } } static inline void voodoo_tmu_fetch(voodoo_t *voodoo, voodoo_params_t *params, voodoo_state_t *state, int tmu, int x) { if (params->textureMode[tmu] & 1) { int64_t _w = 0; if (tmu) { if (state->tmu1_w) _w = (int64_t)((1ULL << 48) / state->tmu1_w); state->tex_s = (int32_t)(((((state->tmu1_s + (1 << 13)) >> 14) * _w) + (1 << 29)) >> 30); state->tex_t = (int32_t)(((((state->tmu1_t + (1 << 13)) >> 14) * _w) + (1 << 29)) >> 30); } else { if (state->tmu0_w) _w = (int64_t)((1ULL << 48) / state->tmu0_w); state->tex_s = (int32_t)(((((state->tmu0_s + (1 << 13)) >> 14) * _w) + (1 << 29)) >> 30); state->tex_t = (int32_t)(((((state->tmu0_t + (1 << 13)) >> 14) * _w) + (1 << 29)) >> 30); } state->lod = state->tmu[tmu].lod + (fastlog(_w) - (19 << 8)); } else { if (tmu) { state->tex_s = (int32_t)(state->tmu1_s >> (14+14)); state->tex_t = (int32_t)(state->tmu1_t >> (14+14)); } else { state->tex_s = (int32_t)(state->tmu0_s >> (14+14)); state->tex_t = (int32_t)(state->tmu0_t >> (14+14)); } state->lod = state->tmu[tmu].lod; } if (state->lod < state->lod_min[tmu]) state->lod = state->lod_min[tmu]; else if (state->lod > state->lod_max[tmu]) state->lod = state->lod_max[tmu]; state->lod_frac[tmu] = state->lod & 0xff; state->lod >>= 8; voodoo_get_texture(voodoo, params, state, tmu, x); } #define DEPTH_TEST(comp_depth) \ do \ { \ switch (depth_op) \ { \ case DEPTHOP_NEVER: \ voodoo->fbiZFuncFail++; \ goto skip_pixel; \ case DEPTHOP_LESSTHAN: \ if (!(comp_depth < old_depth)) \ { \ voodoo->fbiZFuncFail++; \ goto skip_pixel; \ } \ break; \ case DEPTHOP_EQUAL: \ if (!(comp_depth == old_depth)) \ { \ voodoo->fbiZFuncFail++; \ goto skip_pixel; \ } \ break; \ case DEPTHOP_LESSTHANEQUAL: \ if (!(comp_depth <= old_depth)) \ { \ voodoo->fbiZFuncFail++; \ goto skip_pixel; \ } \ break; \ case DEPTHOP_GREATERTHAN: \ if (!(comp_depth > old_depth)) \ { \ voodoo->fbiZFuncFail++; \ goto skip_pixel; \ } \ break; \ case DEPTHOP_NOTEQUAL: \ if (!(comp_depth != old_depth)) \ { \ voodoo->fbiZFuncFail++; \ goto skip_pixel; \ } \ break; \ case DEPTHOP_GREATERTHANEQUAL: \ if (!(comp_depth >= old_depth)) \ { \ voodoo->fbiZFuncFail++; \ goto skip_pixel; \ } \ break; \ case DEPTHOP_ALWAYS: \ break; \ } \ } while (0) #define APPLY_FOG(src_r, src_g, src_b, z, ia, w) \ do \ { \ if (params->fogMode & FOG_CONSTANT) \ { \ src_r += params->fogColor.r; \ src_g += params->fogColor.g; \ src_b += params->fogColor.b; \ } \ else \ { \ int fog_r, fog_g, fog_b, fog_a; \ int fog_idx; \ \ if (!(params->fogMode & FOG_ADD)) \ { \ fog_r = params->fogColor.r; \ fog_g = params->fogColor.g; \ fog_b = params->fogColor.b; \ } \ else \ fog_r = fog_g = fog_b = 0; \ \ if (!(params->fogMode & FOG_MULT)) \ { \ fog_r -= src_r; \ fog_g -= src_g; \ fog_b -= src_b; \ } \ \ switch (params->fogMode & (FOG_Z|FOG_ALPHA)) \ { \ case 0: \ fog_idx = (w_depth >> 10) & 0x3f; \ \ fog_a = params->fogTable[fog_idx].fog; \ fog_a += (params->fogTable[fog_idx].dfog * ((w_depth >> 2) & 0xff)) >> 10; \ break; \ case FOG_Z: \ fog_a = (z >> 20) & 0xff; \ break; \ case FOG_ALPHA: \ fog_a = CLAMP(ia >> 12); \ break; \ case FOG_W: \ fog_a = CLAMP((w >> 32) & 0xff); \ break; \ } \ fog_a++; \ \ fog_r = (fog_r * fog_a) >> 8; \ fog_g = (fog_g * fog_a) >> 8; \ fog_b = (fog_b * fog_a) >> 8; \ \ if (params->fogMode & FOG_MULT) \ { \ src_r = fog_r; \ src_g = fog_g; \ src_b = fog_b; \ } \ else \ { \ src_r += fog_r; \ src_g += fog_g; \ src_b += fog_b; \ } \ } \ \ src_r = CLAMP(src_r); \ src_g = CLAMP(src_g); \ src_b = CLAMP(src_b); \ } while (0) #define ALPHA_TEST(src_a) \ do \ { \ switch (alpha_func) \ { \ case AFUNC_NEVER: \ voodoo->fbiAFuncFail++; \ goto skip_pixel; \ case AFUNC_LESSTHAN: \ if (!(src_a < a_ref)) \ { \ voodoo->fbiAFuncFail++; \ goto skip_pixel; \ } \ break; \ case AFUNC_EQUAL: \ if (!(src_a == a_ref)) \ { \ voodoo->fbiAFuncFail++; \ goto skip_pixel; \ } \ break; \ case AFUNC_LESSTHANEQUAL: \ if (!(src_a <= a_ref)) \ { \ voodoo->fbiAFuncFail++; \ goto skip_pixel; \ } \ break; \ case AFUNC_GREATERTHAN: \ if (!(src_a > a_ref)) \ { \ voodoo->fbiAFuncFail++; \ goto skip_pixel; \ } \ break; \ case AFUNC_NOTEQUAL: \ if (!(src_a != a_ref)) \ { \ voodoo->fbiAFuncFail++; \ goto skip_pixel; \ } \ break; \ case AFUNC_GREATERTHANEQUAL: \ if (!(src_a >= a_ref)) \ { \ voodoo->fbiAFuncFail++; \ goto skip_pixel; \ } \ break; \ case AFUNC_ALWAYS: \ break; \ } \ } while (0) #define ALPHA_BLEND(src_r, src_g, src_b, src_a) \ do \ { \ int _a; \ int newdest_r, newdest_g, newdest_b; \ \ switch (dest_afunc) \ { \ case AFUNC_AZERO: \ newdest_r = newdest_g = newdest_b = 0; \ break; \ case AFUNC_ASRC_ALPHA: \ newdest_r = (dest_r * src_a) / 255; \ newdest_g = (dest_g * src_a) / 255; \ newdest_b = (dest_b * src_a) / 255; \ break; \ case AFUNC_A_COLOR: \ newdest_r = (dest_r * src_r) / 255; \ newdest_g = (dest_g * src_g) / 255; \ newdest_b = (dest_b * src_b) / 255; \ break; \ case AFUNC_ADST_ALPHA: \ newdest_r = (dest_r * dest_a) / 255; \ newdest_g = (dest_g * dest_a) / 255; \ newdest_b = (dest_b * dest_a) / 255; \ break; \ case AFUNC_AONE: \ newdest_r = dest_r; \ newdest_g = dest_g; \ newdest_b = dest_b; \ break; \ case AFUNC_AOMSRC_ALPHA: \ newdest_r = (dest_r * (255-src_a)) / 255; \ newdest_g = (dest_g * (255-src_a)) / 255; \ newdest_b = (dest_b * (255-src_a)) / 255; \ break; \ case AFUNC_AOM_COLOR: \ newdest_r = (dest_r * (255-src_r)) / 255; \ newdest_g = (dest_g * (255-src_g)) / 255; \ newdest_b = (dest_b * (255-src_b)) / 255; \ break; \ case AFUNC_AOMDST_ALPHA: \ newdest_r = (dest_r * (255-dest_a)) / 255; \ newdest_g = (dest_g * (255-dest_a)) / 255; \ newdest_b = (dest_b * (255-dest_a)) / 255; \ break; \ case AFUNC_ASATURATE: \ _a = MIN(src_a, 1-dest_a); \ newdest_r = (dest_r * _a) / 255; \ newdest_g = (dest_g * _a) / 255; \ newdest_b = (dest_b * _a) / 255; \ break; \ } \ \ switch (src_afunc) \ { \ case AFUNC_AZERO: \ src_r = src_g = src_b = 0; \ break; \ case AFUNC_ASRC_ALPHA: \ src_r = (src_r * src_a) / 255; \ src_g = (src_g * src_a) / 255; \ src_b = (src_b * src_a) / 255; \ break; \ case AFUNC_A_COLOR: \ src_r = (src_r * dest_r) / 255; \ src_g = (src_g * dest_g) / 255; \ src_b = (src_b * dest_b) / 255; \ break; \ case AFUNC_ADST_ALPHA: \ src_r = (src_r * dest_a) / 255; \ src_g = (src_g * dest_a) / 255; \ src_b = (src_b * dest_a) / 255; \ break; \ case AFUNC_AONE: \ break; \ case AFUNC_AOMSRC_ALPHA: \ src_r = (src_r * (255-src_a)) / 255; \ src_g = (src_g * (255-src_a)) / 255; \ src_b = (src_b * (255-src_a)) / 255; \ break; \ case AFUNC_AOM_COLOR: \ src_r = (src_r * (255-dest_r)) / 255; \ src_g = (src_g * (255-dest_g)) / 255; \ src_b = (src_b * (255-dest_b)) / 255; \ break; \ case AFUNC_AOMDST_ALPHA: \ src_r = (src_r * (255-dest_a)) / 255; \ src_g = (src_g * (255-dest_a)) / 255; \ src_b = (src_b * (255-dest_a)) / 255; \ break; \ case AFUNC_ACOLORBEFOREFOG: \ fatal("AFUNC_ACOLORBEFOREFOG\n"); \ break; \ } \ \ src_r += newdest_r; \ src_g += newdest_g; \ src_b += newdest_b; \ \ src_r = CLAMP(src_r); \ src_g = CLAMP(src_g); \ src_b = CLAMP(src_b); \ } while(0) #define _rgb_sel ( params->fbzColorPath & 3) #define a_sel ( (params->fbzColorPath >> 2) & 3) #define cc_localselect ( params->fbzColorPath & (1 << 4)) #define cca_localselect ( (params->fbzColorPath >> 5) & 3) #define cc_localselect_override ( params->fbzColorPath & (1 << 7)) #define cc_zero_other ( params->fbzColorPath & (1 << 8)) #define cc_sub_clocal ( params->fbzColorPath & (1 << 9)) #define cc_mselect ( (params->fbzColorPath >> 10) & 7) #define cc_reverse_blend ( params->fbzColorPath & (1 << 13)) #define cc_add ( (params->fbzColorPath >> 14) & 3) #define cc_add_alocal ( params->fbzColorPath & (1 << 15)) #define cc_invert_output ( params->fbzColorPath & (1 << 16)) #define cca_zero_other ( params->fbzColorPath & (1 << 17)) #define cca_sub_clocal ( params->fbzColorPath & (1 << 18)) #define cca_mselect ( (params->fbzColorPath >> 19) & 7) #define cca_reverse_blend ( params->fbzColorPath & (1 << 22)) #define cca_add ( (params->fbzColorPath >> 23) & 3) #define cca_invert_output ( params->fbzColorPath & (1 << 25)) #define tc_zero_other (params->textureMode[0] & (1 << 12)) #define tc_sub_clocal (params->textureMode[0] & (1 << 13)) #define tc_mselect ((params->textureMode[0] >> 14) & 7) #define tc_reverse_blend (params->textureMode[0] & (1 << 17)) #define tc_add_clocal (params->textureMode[0] & (1 << 18)) #define tc_add_alocal (params->textureMode[0] & (1 << 19)) #define tc_invert_output (params->textureMode[0] & (1 << 20)) #define tca_zero_other (params->textureMode[0] & (1 << 21)) #define tca_sub_clocal (params->textureMode[0] & (1 << 22)) #define tca_mselect ((params->textureMode[0] >> 23) & 7) #define tca_reverse_blend (params->textureMode[0] & (1 << 26)) #define tca_add_clocal (params->textureMode[0] & (1 << 27)) #define tca_add_alocal (params->textureMode[0] & (1 << 28)) #define tca_invert_output (params->textureMode[0] & (1 << 29)) #define tc_sub_clocal_1 (params->textureMode[1] & (1 << 13)) #define tc_mselect_1 ((params->textureMode[1] >> 14) & 7) #define tc_reverse_blend_1 (params->textureMode[1] & (1 << 17)) #define tc_add_clocal_1 (params->textureMode[1] & (1 << 18)) #define tc_add_alocal_1 (params->textureMode[1] & (1 << 19)) #define tca_sub_clocal_1 (params->textureMode[1] & (1 << 22)) #define tca_mselect_1 ((params->textureMode[1] >> 23) & 7) #define tca_reverse_blend_1 (params->textureMode[1] & (1 << 26)) #define tca_add_clocal_1 (params->textureMode[1] & (1 << 27)) #define tca_add_alocal_1 (params->textureMode[1] & (1 << 28)) #define src_afunc ( (params->alphaMode >> 8) & 0xf) #define dest_afunc ( (params->alphaMode >> 12) & 0xf) #define alpha_func ( (params->alphaMode >> 1) & 7) #define a_ref ( params->alphaMode >> 24) #define depth_op ( (params->fbzMode >> 5) & 7) #define dither ( params->fbzMode & FBZ_DITHER) #define dither2x2 (params->fbzMode & FBZ_DITHER_2x2) /*Perform texture fetch and blending for both TMUs*/ static inline voodoo_tmu_fetch_and_blend(voodoo_t *voodoo, voodoo_params_t *params, voodoo_state_t *state, int x) { int r,g,b,a; int c_reverse, a_reverse; int c_reverse1, a_reverse1; int factor_r, factor_g, factor_b, factor_a; voodoo_tmu_fetch(voodoo, params, state, 1, x); if ((params->textureMode[1] & TEXTUREMODE_TRILINEAR) && (state->lod & 1)) { c_reverse = tc_reverse_blend; a_reverse = tca_reverse_blend; } else { c_reverse = !tc_reverse_blend; a_reverse = !tca_reverse_blend; } c_reverse1 = c_reverse; a_reverse1 = a_reverse; if (tc_sub_clocal_1) { switch (tc_mselect_1) { case TC_MSELECT_ZERO: factor_r = factor_g = factor_b = 0; break; case TC_MSELECT_CLOCAL: factor_r = state->tex_r[1]; factor_g = state->tex_g[1]; factor_b = state->tex_b[1]; break; case TC_MSELECT_AOTHER: factor_r = factor_g = factor_b = 0; break; case TC_MSELECT_ALOCAL: factor_r = factor_g = factor_b = state->tex_a[1]; break; case TC_MSELECT_DETAIL: factor_r = (params->detail_bias[1] - state->lod) << params->detail_scale[1]; if (factor_r > params->detail_max[1]) factor_r = params->detail_max[1]; factor_g = factor_b = factor_r; break; case TC_MSELECT_LOD_FRAC: factor_r = factor_g = factor_b = state->lod_frac[1]; break; } if (!c_reverse) { r = (-state->tex_r[1] * (factor_r + 1)) >> 8; g = (-state->tex_g[1] * (factor_g + 1)) >> 8; b = (-state->tex_b[1] * (factor_b + 1)) >> 8; } else { r = (-state->tex_r[1] * ((factor_r^0xff) + 1)) >> 8; g = (-state->tex_g[1] * ((factor_g^0xff) + 1)) >> 8; b = (-state->tex_b[1] * ((factor_b^0xff) + 1)) >> 8; } if (tc_add_clocal_1) { r += state->tex_r[1]; g += state->tex_g[1]; b += state->tex_b[1]; } else if (tc_add_alocal_1) { r += state->tex_a[1]; g += state->tex_a[1]; b += state->tex_a[1]; } state->tex_r[1] = CLAMP(r); state->tex_g[1] = CLAMP(g); state->tex_b[1] = CLAMP(b); } if (tca_sub_clocal_1) { switch (tca_mselect_1) { case TCA_MSELECT_ZERO: factor_a = 0; break; case TCA_MSELECT_CLOCAL: factor_a = state->tex_a[1]; break; case TCA_MSELECT_AOTHER: factor_a = 0; break; case TCA_MSELECT_ALOCAL: factor_a = state->tex_a[1]; break; case TCA_MSELECT_DETAIL: factor_a = (params->detail_bias[1] - state->lod) << params->detail_scale[1]; if (factor_a > params->detail_max[1]) factor_a = params->detail_max[1]; break; case TCA_MSELECT_LOD_FRAC: factor_a = state->lod_frac[1]; break; } if (!a_reverse) a = (-state->tex_a[1] * ((factor_a ^ 0xff) + 1)) >> 8; else a = (-state->tex_a[1] * (factor_a + 1)) >> 8; if (tca_add_clocal_1 || tca_add_alocal_1) a += state->tex_a[1]; state->tex_a[1] = CLAMP(a); } voodoo_tmu_fetch(voodoo, params, state, 0, x); if ((params->textureMode[0] & TEXTUREMODE_TRILINEAR) && (state->lod & 1)) { c_reverse = tc_reverse_blend; a_reverse = tca_reverse_blend; } else { c_reverse = !tc_reverse_blend; a_reverse = !tca_reverse_blend; } if (!tc_zero_other) { r = state->tex_r[1]; g = state->tex_g[1]; b = state->tex_b[1]; } else r = g = b = 0; if (tc_sub_clocal) { r -= state->tex_r[0]; g -= state->tex_g[0]; b -= state->tex_b[0]; } switch (tc_mselect) { case TC_MSELECT_ZERO: factor_r = factor_g = factor_b = 0; break; case TC_MSELECT_CLOCAL: factor_r = state->tex_r[0]; factor_g = state->tex_g[0]; factor_b = state->tex_b[0]; break; case TC_MSELECT_AOTHER: factor_r = factor_g = factor_b = state->tex_a[1]; break; case TC_MSELECT_ALOCAL: factor_r = factor_g = factor_b = state->tex_a[0]; break; case TC_MSELECT_DETAIL: factor_r = (params->detail_bias[0] - state->lod) << params->detail_scale[0]; if (factor_r > params->detail_max[0]) factor_r = params->detail_max[0]; factor_g = factor_b = factor_r; break; case TC_MSELECT_LOD_FRAC: factor_r = factor_g = factor_b = state->lod_frac[0]; break; } if (!c_reverse) { r = (r * (factor_r + 1)) >> 8; g = (g * (factor_g + 1)) >> 8; b = (b * (factor_b + 1)) >> 8; } else { r = (r * ((factor_r^0xff) + 1)) >> 8; g = (g * ((factor_g^0xff) + 1)) >> 8; b = (b * ((factor_b^0xff) + 1)) >> 8; } if (tc_add_clocal) { r += state->tex_r[0]; g += state->tex_g[0]; b += state->tex_b[0]; } else if (tc_add_alocal) { r += state->tex_a[0]; g += state->tex_a[0]; b += state->tex_a[0]; } if (!tca_zero_other) a = state->tex_a[1]; else a = 0; if (tca_sub_clocal) a -= state->tex_a[0]; switch (tca_mselect) { case TCA_MSELECT_ZERO: factor_a = 0; break; case TCA_MSELECT_CLOCAL: factor_a = state->tex_a[0]; break; case TCA_MSELECT_AOTHER: factor_a = state->tex_a[1]; break; case TCA_MSELECT_ALOCAL: factor_a = state->tex_a[0]; break; case TCA_MSELECT_DETAIL: factor_a = (params->detail_bias[0] - state->lod) << params->detail_scale[0]; if (factor_a > params->detail_max[0]) factor_a = params->detail_max[0]; break; case TCA_MSELECT_LOD_FRAC: factor_a = state->lod_frac[0]; break; } if (a_reverse) a = (a * ((factor_a ^ 0xff) + 1)) >> 8; else a = (a * (factor_a + 1)) >> 8; if (tca_add_clocal || tca_add_alocal) a += state->tex_a[0]; state->tex_r[0] = CLAMP(r); state->tex_g[0] = CLAMP(g); state->tex_b[0] = CLAMP(b); state->tex_a[0] = CLAMP(a); if (tc_invert_output) { state->tex_r[0] ^= 0xff; state->tex_g[0] ^= 0xff; state->tex_b[0] ^= 0xff; } if (tca_invert_output) state->tex_a[0] ^= 0xff; } #if (defined i386 || defined __i386 || defined __i386__ || defined _X86_ || defined WIN32 || defined _WIN32 || defined _WIN32) && !(defined __amd64__) #include "vid_voodoo_codegen_x86.h" #elif (defined __amd64__) #include "vid_voodoo_codegen_x86-64.h" #else #define NO_CODEGEN static int voodoo_recomp = 0; #endif static void voodoo_half_triangle(voodoo_t *voodoo, voodoo_params_t *params, voodoo_state_t *state, int ystart, int yend, int odd_even) { /* int rgb_sel = params->fbzColorPath & 3; int a_sel = (params->fbzColorPath >> 2) & 3; int cc_localselect = params->fbzColorPath & (1 << 4); int cca_localselect = (params->fbzColorPath >> 5) & 3; int cc_localselect_override = params->fbzColorPath & (1 << 7); int cc_zero_other = params->fbzColorPath & (1 << 8); int cc_sub_clocal = params->fbzColorPath & (1 << 9); int cc_mselect = (params->fbzColorPath >> 10) & 7; int cc_reverse_blend = params->fbzColorPath & (1 << 13); int cc_add = (params->fbzColorPath >> 14) & 3; int cc_add_alocal = params->fbzColorPath & (1 << 15); int cc_invert_output = params->fbzColorPath & (1 << 16); int cca_zero_other = params->fbzColorPath & (1 << 17); int cca_sub_clocal = params->fbzColorPath & (1 << 18); int cca_mselect = (params->fbzColorPath >> 19) & 7; int cca_reverse_blend = params->fbzColorPath & (1 << 22); int cca_add = (params->fbzColorPath >> 23) & 3; int cca_invert_output = params->fbzColorPath & (1 << 25); int src_afunc = (params->alphaMode >> 8) & 0xf; int dest_afunc = (params->alphaMode >> 12) & 0xf; int alpha_func = (params->alphaMode >> 1) & 7; int a_ref = params->alphaMode >> 24; int depth_op = (params->fbzMode >> 5) & 7; int dither = params->fbzMode & FBZ_DITHER;*/ int texels; int c; uint8_t (*voodoo_draw)(voodoo_state_t *state, voodoo_params_t *params, int x, int real_y); if ((params->textureMode[0] & TEXTUREMODE_MASK) == TEXTUREMODE_PASSTHROUGH || (params->textureMode[0] & TEXTUREMODE_LOCAL_MASK) == TEXTUREMODE_LOCAL) texels = 1; else texels = 2; state->clamp_s[0] = params->textureMode[0] & TEXTUREMODE_TCLAMPS; state->clamp_t[0] = params->textureMode[0] & TEXTUREMODE_TCLAMPT; state->clamp_s[1] = params->textureMode[1] & TEXTUREMODE_TCLAMPS; state->clamp_t[1] = params->textureMode[1] & TEXTUREMODE_TCLAMPT; // int last_x; // 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 (c = 0; c <= LOD_MAX; c++) { state->tex[0][c] = &voodoo->texture_cache[0][params->tex_entry[0]].data[texture_offset[c]]; state->tex[1][c] = &voodoo->texture_cache[1][params->tex_entry[1]].data[texture_offset[c]]; } state->tformat = params->tformat[0]; state->tex_w_mask[0] = params->tex_w_mask[0]; state->tex_h_mask[0] = params->tex_h_mask[0]; state->tex_shift[0] = params->tex_shift[0]; state->tex_lod[0] = params->tex_lod[0]; state->tex_w_mask[1] = params->tex_w_mask[1]; state->tex_h_mask[1] = params->tex_h_mask[1]; state->tex_shift[1] = params->tex_shift[1]; state->tex_lod[1] = params->tex_lod[1]; if ((params->fbzMode & 1) && (ystart < params->clipLowY)) { int dy = params->clipLowY - ystart; state->base_r += params->dRdY*dy; state->base_g += params->dGdY*dy; state->base_b += params->dBdY*dy; state->base_a += params->dAdY*dy; state->base_z += params->dZdY*dy; state->tmu[0].base_s += params->tmu[0].dSdY*dy; state->tmu[0].base_t += params->tmu[0].dTdY*dy; state->tmu[0].base_w += params->tmu[0].dWdY*dy; state->tmu[1].base_s += params->tmu[1].dSdY*dy; state->tmu[1].base_t += params->tmu[1].dTdY*dy; state->tmu[1].base_w += params->tmu[1].dWdY*dy; state->base_w += params->dWdY*dy; state->xstart += state->dx1*dy; state->xend += state->dx2*dy; ystart = params->clipLowY; } if ((params->fbzMode & 1) && (yend > params->clipHighY)) yend = params->clipHighY; state->y = ystart; // yend--; #ifndef NO_CODEGEN if (voodoo->use_recompiler) voodoo_draw = voodoo_get_block(voodoo, params, state, odd_even); #endif if (voodoo_output) pclog("dxAB=%08x dxBC=%08x dxAC=%08x\n", state->dxAB, state->dxBC, state->dxAC); // pclog("Start %i %i\n", ystart, voodoo->fbzMode & (1 << 17)); for (; state->y < yend; state->y++) { int x, x2; int real_y = (state->y << 4) + 8; int start_x, start_x2; int dx; uint16_t *fb_mem, *aux_mem; state->ir = state->base_r; state->ig = state->base_g; state->ib = state->base_b; state->ia = state->base_a; state->z = state->base_z; state->tmu0_s = state->tmu[0].base_s; state->tmu0_t = state->tmu[0].base_t; state->tmu0_w = state->tmu[0].base_w; state->tmu1_s = state->tmu[1].base_s; state->tmu1_t = state->tmu[1].base_t; state->tmu1_w = state->tmu[1].base_w; state->w = state->base_w; x = (state->vertexAx << 12) + ((state->dxAC * (real_y - state->vertexAy)) >> 4); if (real_y < state->vertexBy) x2 = (state->vertexAx << 12) + ((state->dxAB * (real_y - state->vertexAy)) >> 4); else x2 = (state->vertexBx << 12) + ((state->dxBC * (real_y - state->vertexBy)) >> 4); if (params->fbzMode & (1 << 17)) real_y = (voodoo->v_disp-1) - (real_y >> 4); else real_y >>= 4; if ((real_y & voodoo->odd_even_mask) != odd_even) goto next_line; start_x = x; if (state->xdir > 0) x2 -= (1 << 16); else x -= (1 << 16); dx = ((x + 0x7000) >> 16) - (((state->vertexAx << 12) + 0x7000) >> 16); start_x2 = x + 0x7000; x = (x + 0x7000) >> 16; x2 = (x2 + 0x7000) >> 16; if (voodoo_output) pclog("%03i:%03i : Ax=%08x start_x=%08x dSdX=%016llx dx=%08x s=%08x -> ", x, state->y, state->vertexAx << 8, start_x, params->tmu[0].dTdX, dx, state->tmu0_t); state->ir += (params->dRdX * dx); state->ig += (params->dGdX * dx); state->ib += (params->dBdX * dx); state->ia += (params->dAdX * dx); state->z += (params->dZdX * dx); state->tmu0_s += (params->tmu[0].dSdX * dx); state->tmu0_t += (params->tmu[0].dTdX * dx); state->tmu0_w += (params->tmu[0].dWdX * dx); state->tmu1_s += (params->tmu[1].dSdX * dx); state->tmu1_t += (params->tmu[1].dTdX * dx); state->tmu1_w += (params->tmu[1].dWdX * dx); state->w += (params->dWdX * dx); if (voodoo_output) pclog("%08llx %lli %lli\n", state->tmu0_t, state->tmu0_t >> (18+state->lod), (state->tmu0_t + (1 << 17+state->lod)) >> (18+state->lod)); if (params->fbzMode & 1) { if (state->xdir > 0) { if (x < params->clipLeft) { int dx = params->clipLeft - x; state->ir += params->dRdX*dx; state->ig += params->dGdX*dx; state->ib += params->dBdX*dx; state->ia += params->dAdX*dx; state->z += params->dZdX*dx; state->tmu0_s += params->tmu[0].dSdX*dx; state->tmu0_t += params->tmu[0].dTdX*dx; state->tmu0_w += params->tmu[0].dWdX*dx; state->tmu1_s += params->tmu[1].dSdX*dx; state->tmu1_t += params->tmu[1].dTdX*dx; state->tmu1_w += params->tmu[1].dWdX*dx; state->w += params->dWdX*dx; x = params->clipLeft; } if (x2 > params->clipRight) x2 = params->clipRight; } else { if (x > params->clipRight) { int dx = params->clipRight - x; state->ir += params->dRdX*dx; state->ig += params->dGdX*dx; state->ib += params->dBdX*dx; state->ia += params->dAdX*dx; state->z += params->dZdX*dx; state->tmu0_s += params->tmu[0].dSdX*dx; state->tmu0_t += params->tmu[0].dTdX*dx; state->tmu0_w += params->tmu[0].dWdX*dx; state->tmu1_s += params->tmu[1].dSdX*dx; state->tmu1_t += params->tmu[1].dTdX*dx; state->tmu1_w += params->tmu[1].dWdX*dx; state->w += params->dWdX*dx; x = params->clipRight; } if (x2 < params->clipLeft) x2 = params->clipLeft; } } if (x2 < x && state->xdir > 0) goto next_line; if (x2 > x && state->xdir < 0) goto next_line; state->fb_mem = fb_mem = &voodoo->fb_mem[params->draw_offset + (real_y * voodoo->row_width)]; state->aux_mem = aux_mem = &voodoo->fb_mem[(params->aux_offset + (real_y * voodoo->row_width)) & voodoo->fb_mask]; if (voodoo_output) pclog("%03i: x=%08x x2=%08x xstart=%08x xend=%08x dx=%08x start_x2=%08x\n", state->y, x, x2, state->xstart, state->xend, dx, start_x2); state->pixel_count = 0; state->texel_count = 0; state->x = x; state->x2 = x2; #ifndef NO_CODEGEN if (voodoo->use_recompiler) { voodoo_draw(state, params, x, real_y); } else #endif do { start_x = x; state->x = x; voodoo->pixel_count[odd_even]++; voodoo->texel_count[odd_even] += texels; voodoo->fbiPixelsIn++; if (voodoo_output) pclog(" X=%03i T=%08x\n", x, state->tmu0_t); // if (voodoo->fbzMode & FBZ_RGB_WMASK) { int update = 1; uint8_t cother_r, cother_g, cother_b, aother; uint8_t clocal_r, clocal_g, clocal_b, alocal; int src_r, src_g, src_b, src_a; int msel_r, msel_g, msel_b, msel_a; uint8_t dest_r, dest_g, dest_b, dest_a; uint16_t dat; uint16_t aux_dat; int sel; int32_t new_depth, w_depth; if (state->w & 0xffff00000000) w_depth = 0; else if (!(state->w & 0xffff0000)) w_depth = 0xf001; else { int exp = fls((uint16_t)((uint32_t)state->w >> 16)); int mant = ((~(uint32_t)state->w >> (19 - exp))) & 0xfff; w_depth = (exp << 12) + mant + 1; if (w_depth > 0xffff) w_depth = 0xffff; } // w_depth = CLAMP16(w_depth); if (params->fbzMode & FBZ_W_BUFFER) new_depth = w_depth; else new_depth = CLAMP16(state->z >> 12); if (params->fbzMode & FBZ_DEPTH_BIAS) new_depth = CLAMP16(new_depth + (int16_t)params->zaColor); if (params->fbzMode & FBZ_DEPTH_ENABLE) { uint16_t old_depth = aux_mem[x]; DEPTH_TEST((params->fbzMode & FBZ_DEPTH_SOURCE) ? (params->zaColor & 0xffff) : new_depth); } dat = fb_mem[x]; dest_r = (dat >> 8) & 0xf8; dest_g = (dat >> 3) & 0xfc; dest_b = (dat << 3) & 0xf8; dest_r |= (dest_r >> 5); dest_g |= (dest_g >> 6); dest_b |= (dest_b >> 5); dest_a = 0xff; if (params->fbzColorPath & FBZCP_TEXTURE_ENABLED) { if ((params->textureMode[0] & TEXTUREMODE_LOCAL_MASK) == TEXTUREMODE_LOCAL || !voodoo->dual_tmus) { /*TMU0 only sampling local colour or only one TMU, only sample TMU0*/ voodoo_tmu_fetch(voodoo, params, state, 0, x); } else if ((params->textureMode[0] & TEXTUREMODE_MASK) == TEXTUREMODE_PASSTHROUGH) { /*TMU0 in pass-through mode, only sample TMU1*/ voodoo_tmu_fetch(voodoo, params, state, 1, x); state->tex_r[0] = state->tex_r[1]; state->tex_g[0] = state->tex_g[1]; state->tex_b[0] = state->tex_b[1]; state->tex_a[0] = state->tex_a[1]; } else { voodoo_tmu_fetch_and_blend(voodoo, params, state, x); } if ((params->fbzMode & FBZ_CHROMAKEY) && state->tex_r[0] == params->chromaKey_r && state->tex_g[0] == params->chromaKey_g && state->tex_b[0] == params->chromaKey_b) { voodoo->fbiChromaFail++; goto skip_pixel; } } if (voodoo->trexInit1[0] & (1 << 18)) { state->tex_r[0] = state->tex_g[0] = 0; if (voodoo->dual_tmus) state->tex_b[0] = 1 | (3 << 6); else state->tex_b[0] = 1; } if (cc_localselect_override) sel = (state->tex_a[0] & 0x80) ? 1 : 0; else sel = cc_localselect; if (sel) { clocal_r = (params->color0 >> 16) & 0xff; clocal_g = (params->color0 >> 8) & 0xff; clocal_b = params->color0 & 0xff; } else { clocal_r = CLAMP(state->ir >> 12); clocal_g = CLAMP(state->ig >> 12); clocal_b = CLAMP(state->ib >> 12); } switch (_rgb_sel) { case CC_LOCALSELECT_ITER_RGB: /*Iterated RGB*/ cother_r = CLAMP(state->ir >> 12); cother_g = CLAMP(state->ig >> 12); cother_b = CLAMP(state->ib >> 12); break; case CC_LOCALSELECT_TEX: /*TREX Color Output*/ cother_r = state->tex_r[0]; cother_g = state->tex_g[0]; cother_b = state->tex_b[0]; break; case CC_LOCALSELECT_COLOR1: /*Color1 RGB*/ cother_r = (params->color1 >> 16) & 0xff; cother_g = (params->color1 >> 8) & 0xff; cother_b = params->color1 & 0xff; break; case CC_LOCALSELECT_LFB: /*Linear Frame Buffer*/ cother_r = src_r; cother_g = src_g; cother_b = src_b; break; } switch (cca_localselect) { case CCA_LOCALSELECT_ITER_A: alocal = CLAMP(state->ia >> 12); break; case CCA_LOCALSELECT_COLOR0: alocal = (params->color0 >> 24) & 0xff; break; case CCA_LOCALSELECT_ITER_Z: alocal = CLAMP(state->z >> 20); break; default: fatal("Bad cca_localselect %i\n", cca_localselect); alocal = 0xff; break; } switch (a_sel) { case A_SEL_ITER_A: aother = CLAMP(state->ia >> 12); break; case A_SEL_TEX: aother = state->tex_a[0]; break; case A_SEL_COLOR1: aother = (params->color1 >> 24) & 0xff; break; default: fatal("Bad a_sel %i\n", a_sel); aother = 0; break; } if (cc_zero_other) { src_r = 0; src_g = 0; src_b = 0; } else { src_r = cother_r; src_g = cother_g; src_b = cother_b; } if (cca_zero_other) src_a = 0; else src_a = aother; if (cc_sub_clocal) { src_r -= clocal_r; src_g -= clocal_g; src_b -= clocal_b; } if (cca_sub_clocal) src_a -= alocal; switch (cc_mselect) { case CC_MSELECT_ZERO: msel_r = 0; msel_g = 0; msel_b = 0; break; case CC_MSELECT_CLOCAL: msel_r = clocal_r; msel_g = clocal_g; msel_b = clocal_b; break; case CC_MSELECT_AOTHER: msel_r = aother; msel_g = aother; msel_b = aother; break; case CC_MSELECT_ALOCAL: msel_r = alocal; msel_g = alocal; msel_b = alocal; break; case CC_MSELECT_TEX: msel_r = state->tex_a[0]; msel_g = state->tex_a[0]; msel_b = state->tex_a[0]; break; case CC_MSELECT_TEXRGB: msel_r = state->tex_r[0]; msel_g = state->tex_g[0]; msel_b = state->tex_b[0]; break; default: fatal("Bad cc_mselect %i\n", cc_mselect); msel_r = 0; msel_g = 0; msel_b = 0; break; } switch (cca_mselect) { case CCA_MSELECT_ZERO: msel_a = 0; break; case CCA_MSELECT_ALOCAL: msel_a = alocal; break; case CCA_MSELECT_AOTHER: msel_a = aother; break; case CCA_MSELECT_ALOCAL2: msel_a = alocal; break; case CCA_MSELECT_TEX: msel_a = state->tex_a[0]; break; default: fatal("Bad cca_mselect %i\n", cca_mselect); msel_a = 0; break; } if (!cc_reverse_blend) { msel_r ^= 0xff; msel_g ^= 0xff; msel_b ^= 0xff; } msel_r++; msel_g++; msel_b++; if (!cca_reverse_blend) msel_a ^= 0xff; msel_a++; src_r = (src_r * msel_r) >> 8; src_g = (src_g * msel_g) >> 8; src_b = (src_b * msel_b) >> 8; src_a = (src_a * msel_a) >> 8; switch (cc_add) { case CC_ADD_CLOCAL: src_r += clocal_r; src_g += clocal_g; src_b += clocal_b; break; case CC_ADD_ALOCAL: src_r += alocal; src_g += alocal; src_b += alocal; break; case 0: break; default: fatal("Bad cc_add %i\n", cc_add); } if (cca_add) src_a += alocal; src_r = CLAMP(src_r); src_g = CLAMP(src_g); src_b = CLAMP(src_b); src_a = CLAMP(src_a); if (cc_invert_output) { src_r ^= 0xff; src_g ^= 0xff; src_b ^= 0xff; } if (cca_invert_output) src_a ^= 0xff; if (params->fogMode & FOG_ENABLE) APPLY_FOG(src_r, src_g, src_b, state->z, state->ia, state->w); if (params->alphaMode & 1) ALPHA_TEST(src_a); if (params->alphaMode & (1 << 4)) ALPHA_BLEND(src_r, src_g, src_b, src_a); if (update) { if (dither) { if (dither2x2) { src_r = dither_rb2x2[src_r][real_y & 1][x & 1]; src_g = dither_g2x2[src_g][real_y & 1][x & 1]; src_b = dither_rb2x2[src_b][real_y & 1][x & 1]; } else { src_r = dither_rb[src_r][real_y & 3][x & 3]; src_g = dither_g[src_g][real_y & 3][x & 3]; src_b = dither_rb[src_b][real_y & 3][x & 3]; } } else { src_r >>= 3; src_g >>= 2; src_b >>= 3; } if (params->fbzMode & FBZ_RGB_WMASK) fb_mem[x] = src_b | (src_g << 5) | (src_r << 11); if ((params->fbzMode & (FBZ_DEPTH_WMASK | FBZ_DEPTH_ENABLE)) == (FBZ_DEPTH_WMASK | FBZ_DEPTH_ENABLE)) aux_mem[x] = new_depth; } } voodoo_output &= ~2; voodoo->fbiPixelsOut++; skip_pixel: if (state->xdir > 0) { state->ir += params->dRdX; state->ig += params->dGdX; state->ib += params->dBdX; state->ia += params->dAdX; state->z += params->dZdX; state->tmu0_s += params->tmu[0].dSdX; state->tmu0_t += params->tmu[0].dTdX; state->tmu0_w += params->tmu[0].dWdX; state->tmu1_s += params->tmu[1].dSdX; state->tmu1_t += params->tmu[1].dTdX; state->tmu1_w += params->tmu[1].dWdX; state->w += params->dWdX; } else { state->ir -= params->dRdX; state->ig -= params->dGdX; state->ib -= params->dBdX; state->ia -= params->dAdX; state->z -= params->dZdX; state->tmu0_s -= params->tmu[0].dSdX; state->tmu0_t -= params->tmu[0].dTdX; state->tmu0_w -= params->tmu[0].dWdX; state->tmu1_s -= params->tmu[1].dSdX; state->tmu1_t -= params->tmu[1].dTdX; state->tmu1_w -= params->tmu[1].dWdX; state->w -= params->dWdX; } x += state->xdir; } while (start_x != x2); voodoo->pixel_count[odd_even] += state->pixel_count; voodoo->texel_count[odd_even] += state->texel_count; voodoo->fbiPixelsIn += state->pixel_count; if (voodoo->params.draw_offset == voodoo->params.front_offset) voodoo->dirty_line[real_y] = 1; next_line: state->base_r += params->dRdY; state->base_g += params->dGdY; state->base_b += params->dBdY; state->base_a += params->dAdY; state->base_z += params->dZdY; state->tmu[0].base_s += params->tmu[0].dSdY; state->tmu[0].base_t += params->tmu[0].dTdY; state->tmu[0].base_w += params->tmu[0].dWdY; state->tmu[1].base_s += params->tmu[1].dSdY; state->tmu[1].base_t += params->tmu[1].dTdY; state->tmu[1].base_w += params->tmu[1].dWdY; state->base_w += params->dWdY; state->xstart += state->dx1; state->xend += state->dx2; } voodoo->texture_cache[0][params->tex_entry[0]].refcount_r[odd_even]++; voodoo->texture_cache[1][params->tex_entry[1]].refcount_r[odd_even]++; } static void voodoo_triangle(voodoo_t *voodoo, voodoo_params_t *params, int odd_even) { voodoo_state_t state; int vertexAy_adjusted; int vertexBy_adjusted; int vertexCy_adjusted; int dx, dy; uint64_t tempdx, tempdy; uint64_t tempLOD; int LOD; int lodbias; voodoo->tri_count++; dx = 8 - (params->vertexAx & 0xf); if ((params->vertexAx & 0xf) > 8) dx += 16; dy = 8 - (params->vertexAy & 0xf); if ((params->vertexAy & 0xf) > 8) dy += 16; /* pclog("voodoo_triangle %i %i %i : vA %f, %f vB %f, %f vC %f, %f f %i,%i %08x %08x %08x,%08x tex=%i,%i fogMode=%08x\n", odd_even, voodoo->params_read_idx[odd_even], voodoo->params_read_idx[odd_even] & PARAM_MASK, (float)params->vertexAx / 16.0, (float)params->vertexAy / 16.0, (float)params->vertexBx / 16.0, (float)params->vertexBy / 16.0, (float)params->vertexCx / 16.0, (float)params->vertexCy / 16.0, (params->fbzColorPath & FBZCP_TEXTURE_ENABLED) ? params->tformat[0] : 0, (params->fbzColorPath & FBZCP_TEXTURE_ENABLED) ? params->tformat[1] : 0, params->fbzColorPath, params->alphaMode, params->textureMode[0],params->textureMode[1], params->tex_entry[0],params->tex_entry[1], params->fogMode);*/ state.base_r = params->startR; state.base_g = params->startG; state.base_b = params->startB; state.base_a = params->startA; state.base_z = params->startZ; state.tmu[0].base_s = params->tmu[0].startS; state.tmu[0].base_t = params->tmu[0].startT; state.tmu[0].base_w = params->tmu[0].startW; state.tmu[1].base_s = params->tmu[1].startS; state.tmu[1].base_t = params->tmu[1].startT; state.tmu[1].base_w = params->tmu[1].startW; state.base_w = params->startW; if (params->fbzColorPath & FBZ_PARAM_ADJUST) { state.base_r += (dx*params->dRdX + dy*params->dRdY) >> 4; state.base_g += (dx*params->dGdX + dy*params->dGdY) >> 4; state.base_b += (dx*params->dBdX + dy*params->dBdY) >> 4; state.base_a += (dx*params->dAdX + dy*params->dAdY) >> 4; state.base_z += (dx*params->dZdX + dy*params->dZdY) >> 4; state.tmu[0].base_s += (dx*params->tmu[0].dSdX + dy*params->tmu[0].dSdY) >> 4; state.tmu[0].base_t += (dx*params->tmu[0].dTdX + dy*params->tmu[0].dTdY) >> 4; state.tmu[0].base_w += (dx*params->tmu[0].dWdX + dy*params->tmu[0].dWdY) >> 4; state.tmu[1].base_s += (dx*params->tmu[1].dSdX + dy*params->tmu[1].dSdY) >> 4; state.tmu[1].base_t += (dx*params->tmu[1].dTdX + dy*params->tmu[1].dTdY) >> 4; state.tmu[1].base_w += (dx*params->tmu[1].dWdX + dy*params->tmu[1].dWdY) >> 4; state.base_w += (dx*params->dWdX + dy*params->dWdY) >> 4; } tris++; state.vertexAy = params->vertexAy & ~0xffff0000; if (state.vertexAy & 0x8000) state.vertexAy |= 0xffff0000; state.vertexBy = params->vertexBy & ~0xffff0000; if (state.vertexBy & 0x8000) state.vertexBy |= 0xffff0000; state.vertexCy = params->vertexCy & ~0xffff0000; if (state.vertexCy & 0x8000) state.vertexCy |= 0xffff0000; state.vertexAx = params->vertexAx & ~0xffff0000; if (state.vertexAx & 0x8000) state.vertexAx |= 0xffff0000; state.vertexBx = params->vertexBx & ~0xffff0000; if (state.vertexBx & 0x8000) state.vertexBx |= 0xffff0000; state.vertexCx = params->vertexCx & ~0xffff0000; if (state.vertexCx & 0x8000) state.vertexCx |= 0xffff0000; vertexAy_adjusted = (state.vertexAy+7) >> 4; vertexBy_adjusted = (state.vertexBy+7) >> 4; vertexCy_adjusted = (state.vertexCy+7) >> 4; if (state.vertexBy - state.vertexAy) state.dxAB = (int)((((int64_t)state.vertexBx << 12) - ((int64_t)state.vertexAx << 12)) << 4) / (int)(state.vertexBy - state.vertexAy); else state.dxAB = 0; if (state.vertexCy - state.vertexAy) state.dxAC = (int)((((int64_t)state.vertexCx << 12) - ((int64_t)state.vertexAx << 12)) << 4) / (int)(state.vertexCy - state.vertexAy); else state.dxAC = 0; if (state.vertexCy - state.vertexBy) state.dxBC = (int)((((int64_t)state.vertexCx << 12) - ((int64_t)state.vertexBx << 12)) << 4) / (int)(state.vertexCy - state.vertexBy); else state.dxBC = 0; state.lod_min[0] = (params->tLOD[0] & 0x3f) << 6; state.lod_max[0] = ((params->tLOD[0] >> 6) & 0x3f) << 6; if (state.lod_max[0] > 0x800) state.lod_max[0] = 0x800; state.lod_min[1] = (params->tLOD[1] & 0x3f) << 6; state.lod_max[1] = ((params->tLOD[1] >> 6) & 0x3f) << 6; if (state.lod_max[1] > 0x800) state.lod_max[1] = 0x800; state.xstart = state.xend = state.vertexAx << 8; state.xdir = params->sign ? -1 : 1; state.y = (state.vertexAy + 8) >> 4; state.ydir = 1; tempdx = (params->tmu[0].dSdX >> 14) * (params->tmu[0].dSdX >> 14) + (params->tmu[0].dTdX >> 14) * (params->tmu[0].dTdX >> 14); tempdy = (params->tmu[0].dSdY >> 14) * (params->tmu[0].dSdY >> 14) + (params->tmu[0].dTdY >> 14) * (params->tmu[0].dTdY >> 14); if (tempdx > tempdy) tempLOD = tempdx; else tempLOD = tempdy; LOD = (int)(log2((double)tempLOD / (double)(1ULL << 36)) * 256); LOD >>= 2; lodbias = (params->tLOD[0] >> 12) & 0x3f; if (lodbias & 0x20) lodbias |= ~0x3f; state.tmu[0].lod = LOD + (lodbias << 6); tempdx = (params->tmu[1].dSdX >> 14) * (params->tmu[1].dSdX >> 14) + (params->tmu[1].dTdX >> 14) * (params->tmu[1].dTdX >> 14); tempdy = (params->tmu[1].dSdY >> 14) * (params->tmu[1].dSdY >> 14) + (params->tmu[1].dTdY >> 14) * (params->tmu[1].dTdY >> 14); if (tempdx > tempdy) tempLOD = tempdx; else tempLOD = tempdy; LOD = (int)(log2((double)tempLOD / (double)(1ULL << 36)) * 256); LOD >>= 2; lodbias = (params->tLOD[1] >> 12) & 0x3f; if (lodbias & 0x20) lodbias |= ~0x3f; state.tmu[1].lod = LOD + (lodbias << 6); voodoo_half_triangle(voodoo, params, &state, vertexAy_adjusted, vertexCy_adjusted, odd_even); } static inline void wake_render_thread(voodoo_t *voodoo) { thread_set_event(voodoo->wake_render_thread[0]); /*Wake up render thread if moving from idle*/ if (voodoo->render_threads == 2) thread_set_event(voodoo->wake_render_thread[1]); /*Wake up render thread if moving from idle*/ } static inline void wait_for_render_thread_idle(voodoo_t *voodoo) { while (!PARAM_EMPTY_1 || (voodoo->render_threads == 2 && !PARAM_EMPTY_2) || voodoo->render_voodoo_busy[0] || (voodoo->render_threads == 2 && voodoo->render_voodoo_busy[1])) { wake_render_thread(voodoo); if (!PARAM_EMPTY_1 || voodoo->render_voodoo_busy[0]) thread_wait_event(voodoo->render_not_full_event[0], 1); if (voodoo->render_threads == 2 && (!PARAM_EMPTY_2 || voodoo->render_voodoo_busy[1])) thread_wait_event(voodoo->render_not_full_event[1], 1); } } static void render_thread(void *param, int odd_even) { voodoo_t *voodoo = (voodoo_t *)param; while (1) { thread_set_event(voodoo->render_not_full_event[odd_even]); thread_wait_event(voodoo->wake_render_thread[odd_even], -1); thread_reset_event(voodoo->wake_render_thread[odd_even]); voodoo->render_voodoo_busy[odd_even] = 1; while (!(odd_even ? PARAM_EMPTY_2 : PARAM_EMPTY_1)) { uint64_t start_time = timer_read(); uint64_t end_time; voodoo_params_t *params = &voodoo->params_buffer[voodoo->params_read_idx[odd_even] & PARAM_MASK]; voodoo_triangle(voodoo, params, odd_even); voodoo->params_read_idx[odd_even]++; if ((odd_even ? PARAM_ENTRIES_2 : PARAM_ENTRIES_1) > (PARAM_SIZE - 10)) thread_set_event(voodoo->render_not_full_event[odd_even]); end_time = timer_read(); voodoo_render_time[odd_even] += end_time - start_time; } voodoo->render_voodoo_busy[odd_even] = 0; } } static void render_thread_1(void *param) { render_thread(param, 0); } static void render_thread_2(void *param) { render_thread(param, 1); } static inline void queue_triangle(voodoo_t *voodoo, voodoo_params_t *params) { voodoo_params_t *params_new = &voodoo->params_buffer[voodoo->params_write_idx & PARAM_MASK]; while (PARAM_FULL_1 || (voodoo->render_threads == 2 && PARAM_FULL_2)) { thread_reset_event(voodoo->render_not_full_event[0]); if (voodoo->render_threads == 2) thread_reset_event(voodoo->render_not_full_event[1]); if (PARAM_FULL_1) { thread_wait_event(voodoo->render_not_full_event[0], -1); /*Wait for room in ringbuffer*/ } if (voodoo->render_threads == 2 && PARAM_FULL_2) { thread_wait_event(voodoo->render_not_full_event[1], -1); /*Wait for room in ringbuffer*/ } } use_texture(voodoo, params, 0); if (voodoo->dual_tmus) use_texture(voodoo, params, 1); memcpy(params_new, params, sizeof(voodoo_params_t)); voodoo->params_write_idx++; if (PARAM_ENTRIES_1 < 4 || (voodoo->render_threads == 2 && PARAM_ENTRIES_2 < 4)) wake_render_thread(voodoo); } static void voodoo_fastfill(voodoo_t *voodoo, voodoo_params_t *params) { int y; int low_y, high_y; if (params->fbzMode & (1 << 17)) { high_y = voodoo->v_disp - params->clipLowY; low_y = voodoo->v_disp - params->clipHighY; } else { low_y = params->clipLowY; high_y = params->clipHighY; } if (params->fbzMode & FBZ_RGB_WMASK) { int r, g, b; uint16_t col; r = ((params->color1 >> 16) >> 3) & 0x1f; g = ((params->color1 >> 8) >> 2) & 0x3f; b = (params->color1 >> 3) & 0x1f; col = b | (g << 5) | (r << 11); for (y = low_y; y < high_y; y++) { uint16_t *cbuf = (uint16_t *)&voodoo->fb_mem[(params->draw_offset + y*voodoo->row_width) & voodoo->fb_mask]; int x; for (x = params->clipLeft; x < params->clipRight; x++) cbuf[x] = col; } } if (params->fbzMode & FBZ_DEPTH_WMASK) { for (y = low_y; y < high_y; y++) { uint16_t *abuf = (uint16_t *)&voodoo->fb_mem[(params->aux_offset + y*voodoo->row_width) & voodoo->fb_mask]; int x; for (x = params->clipLeft; x < params->clipRight; x++) abuf[x] = params->zaColor & 0xffff; } } } enum { SETUPMODE_RGB = (1 << 0), SETUPMODE_ALPHA = (1 << 1), SETUPMODE_Z = (1 << 2), SETUPMODE_Wb = (1 << 3), SETUPMODE_W0 = (1 << 4), SETUPMODE_S0_T0 = (1 << 5), SETUPMODE_W1 = (1 << 6), SETUPMODE_S1_T1 = (1 << 7), SETUPMODE_STRIP_MODE = (1 << 16), SETUPMODE_CULLING_ENABLE = (1 << 17), SETUPMODE_CULLING_SIGN = (1 << 18), SETUPMODE_DISABLE_PINGPONG = (1 << 19) }; static void triangle_setup(voodoo_t *voodoo) { float dxAB, dxBC, dyAB, dyBC; float area; int va = 0, vb = 1, vc = 2; int reverse_cull = 0; if (voodoo->verts[0].sVy < voodoo->verts[1].sVy) { if (voodoo->verts[1].sVy < voodoo->verts[2].sVy) { /* V1>V0, V2>V1, V2>V1>V0*/ va = 0; /*OK*/ vb = 1; vc = 2; } else { /* V1>V0, V1>V2*/ if (voodoo->verts[0].sVy < voodoo->verts[2].sVy) { /* V1>V0, V1>V2, V2>V0, V1>V2>V0*/ va = 0; vb = 2; vc = 1; reverse_cull = 1; } else { /* V1>V0, V1>V2, V0>V2, V1>V0>V2*/ va = 2; vb = 0; vc = 1; } } } else { if (voodoo->verts[1].sVy < voodoo->verts[2].sVy) { /* V0>V1, V2>V1*/ if (voodoo->verts[0].sVy < voodoo->verts[2].sVy) { /* V0>V1, V2>V1, V2>V0, V2>V0>V1*/ va = 1; vb = 0; vc = 2; reverse_cull = 1; } else { /* V0>V1, V2>V1, V0>V2, V0>V2>V1*/ va = 1; vb = 2; vc = 0; } } else { /*V0>V1>V2*/ va = 2; vb = 1; vc = 0; reverse_cull = 1; } } dxAB = voodoo->verts[va].sVx - voodoo->verts[vb].sVx; dxBC = voodoo->verts[vb].sVx - voodoo->verts[vc].sVx; dyAB = voodoo->verts[va].sVy - voodoo->verts[vb].sVy; dyBC = voodoo->verts[vb].sVy - voodoo->verts[vc].sVy; area = dxAB * dyBC - dxBC * dyAB; if (area == 0.0) { if ((voodoo->sSetupMode & SETUPMODE_CULLING_ENABLE) && !(voodoo->sSetupMode & SETUPMODE_DISABLE_PINGPONG)) voodoo->sSetupMode ^= SETUPMODE_CULLING_SIGN; return; } dxAB /= area; dxBC /= area; dyAB /= area; dyBC /= area; if (voodoo->sSetupMode & SETUPMODE_CULLING_ENABLE) { int cull_sign = voodoo->sSetupMode & SETUPMODE_CULLING_SIGN; int sign = (area < 0.0); if (!(voodoo->sSetupMode & SETUPMODE_DISABLE_PINGPONG)) voodoo->sSetupMode ^= SETUPMODE_CULLING_SIGN; if (reverse_cull) sign = !sign; if (cull_sign && sign) return; if (!cull_sign && !sign) return; } voodoo->params.vertexAx = (int32_t)(int16_t)((int32_t)(voodoo->verts[va].sVx * 16.0f) & 0xffff); voodoo->params.vertexAy = (int32_t)(int16_t)((int32_t)(voodoo->verts[va].sVy * 16.0f) & 0xffff); voodoo->params.vertexBx = (int32_t)(int16_t)((int32_t)(voodoo->verts[vb].sVx * 16.0f) & 0xffff); voodoo->params.vertexBy = (int32_t)(int16_t)((int32_t)(voodoo->verts[vb].sVy * 16.0f) & 0xffff); voodoo->params.vertexCx = (int32_t)(int16_t)((int32_t)(voodoo->verts[vc].sVx * 16.0f) & 0xffff); voodoo->params.vertexCy = (int32_t)(int16_t)((int32_t)(voodoo->verts[vc].sVy * 16.0f) & 0xffff); if (voodoo->params.vertexAy > voodoo->params.vertexBy || voodoo->params.vertexBy > voodoo->params.vertexCy) fatal("triangle_setup wrong order %d %d %d\n", voodoo->params.vertexAy, voodoo->params.vertexBy, voodoo->params.vertexCy); if (voodoo->sSetupMode & SETUPMODE_RGB) { voodoo->params.startR = (int32_t)(voodoo->verts[va].sRed * 4096.0f); voodoo->params.dRdX = (int32_t)(((voodoo->verts[va].sRed - voodoo->verts[vb].sRed) * dyBC - (voodoo->verts[vb].sRed - voodoo->verts[vc].sRed) * dyAB) * 4096.0f); voodoo->params.dRdY = (int32_t)(((voodoo->verts[vb].sRed - voodoo->verts[vc].sRed) * dxAB - (voodoo->verts[va].sRed - voodoo->verts[vb].sRed) * dxBC) * 4096.0f); voodoo->params.startG = (int32_t)(voodoo->verts[va].sGreen * 4096.0f); voodoo->params.dGdX = (int32_t)(((voodoo->verts[va].sGreen - voodoo->verts[vb].sGreen) * dyBC - (voodoo->verts[vb].sGreen - voodoo->verts[vc].sGreen) * dyAB) * 4096.0f); voodoo->params.dGdY = (int32_t)(((voodoo->verts[vb].sGreen - voodoo->verts[vc].sGreen) * dxAB - (voodoo->verts[va].sGreen - voodoo->verts[vb].sGreen) * dxBC) * 4096.0f); voodoo->params.startB = (int32_t)(voodoo->verts[va].sBlue * 4096.0f); voodoo->params.dBdX = (int32_t)(((voodoo->verts[va].sBlue - voodoo->verts[vb].sBlue) * dyBC - (voodoo->verts[vb].sBlue - voodoo->verts[vc].sBlue) * dyAB) * 4096.0f); voodoo->params.dBdY = (int32_t)(((voodoo->verts[vb].sBlue - voodoo->verts[vc].sBlue) * dxAB - (voodoo->verts[va].sBlue - voodoo->verts[vb].sBlue) * dxBC) * 4096.0f); } if (voodoo->sSetupMode & SETUPMODE_ALPHA) { voodoo->params.startA = (int32_t)(voodoo->verts[va].sAlpha * 4096.0f); voodoo->params.dAdX = (int32_t)(((voodoo->verts[va].sAlpha - voodoo->verts[vb].sAlpha) * dyBC - (voodoo->verts[vb].sAlpha - voodoo->verts[vc].sAlpha) * dyAB) * 4096.0f); voodoo->params.dAdY = (int32_t)(((voodoo->verts[vb].sAlpha - voodoo->verts[vc].sAlpha) * dxAB - (voodoo->verts[va].sAlpha - voodoo->verts[vb].sAlpha) * dxBC) * 4096.0f); } if (voodoo->sSetupMode & SETUPMODE_Z) { voodoo->params.startZ = (int32_t)(voodoo->verts[va].sVz * 4096.0f); voodoo->params.dZdX = (int32_t)(((voodoo->verts[va].sVz - voodoo->verts[vb].sVz) * dyBC - (voodoo->verts[vb].sVz - voodoo->verts[vc].sVz) * dyAB) * 4096.0f); voodoo->params.dZdY = (int32_t)(((voodoo->verts[vb].sVz - voodoo->verts[vc].sVz) * dxAB - (voodoo->verts[va].sVz - voodoo->verts[vb].sVz) * dxBC) * 4096.0f); } if (voodoo->sSetupMode & SETUPMODE_Wb) { voodoo->params.startW = (int64_t)(voodoo->verts[va].sWb * 4294967296.0f); voodoo->params.dWdX = (int64_t)(((voodoo->verts[va].sWb - voodoo->verts[vb].sWb) * dyBC - (voodoo->verts[vb].sWb - voodoo->verts[vc].sWb) * dyAB) * 4294967296.0f); voodoo->params.dWdY = (int64_t)(((voodoo->verts[vb].sWb - voodoo->verts[vc].sWb) * dxAB - (voodoo->verts[va].sWb - voodoo->verts[vb].sWb) * dxBC) * 4294967296.0f); voodoo->params.tmu[0].startW = voodoo->params.tmu[1].startW = voodoo->params.startW; voodoo->params.tmu[0].dWdX = voodoo->params.tmu[1].dWdX = voodoo->params.dWdX; voodoo->params.tmu[0].dWdY = voodoo->params.tmu[1].dWdY = voodoo->params.dWdY; } if (voodoo->sSetupMode & SETUPMODE_W0) { voodoo->params.tmu[0].startW = (int64_t)(voodoo->verts[va].sW0 * 4294967296.0f); voodoo->params.tmu[0].dWdX = (int64_t)(((voodoo->verts[va].sW0 - voodoo->verts[vb].sW0) * dyBC - (voodoo->verts[vb].sW0 - voodoo->verts[vc].sW0) * dyAB) * 4294967296.0f); voodoo->params.tmu[0].dWdY = (int64_t)(((voodoo->verts[vb].sW0 - voodoo->verts[vc].sW0) * dxAB - (voodoo->verts[va].sW0 - voodoo->verts[vb].sW0) * dxBC) * 4294967296.0f); voodoo->params.tmu[1].startW = voodoo->params.tmu[0].startW; voodoo->params.tmu[1].dWdX = voodoo->params.tmu[0].dWdX; voodoo->params.tmu[1].dWdY = voodoo->params.tmu[0].dWdY; } if (voodoo->sSetupMode & SETUPMODE_S0_T0) { voodoo->params.tmu[0].startS = (int64_t)(voodoo->verts[va].sS0 * 4294967296.0f); voodoo->params.tmu[0].dSdX = (int64_t)(((voodoo->verts[va].sS0 - voodoo->verts[vb].sS0) * dyBC - (voodoo->verts[vb].sS0 - voodoo->verts[vc].sS0) * dyAB) * 4294967296.0f); voodoo->params.tmu[0].dSdY = (int64_t)(((voodoo->verts[vb].sS0 - voodoo->verts[vc].sS0) * dxAB - (voodoo->verts[va].sS0 - voodoo->verts[vb].sS0) * dxBC) * 4294967296.0f); voodoo->params.tmu[0].startT = (int64_t)(voodoo->verts[va].sT0 * 4294967296.0f); voodoo->params.tmu[0].dTdX = (int64_t)(((voodoo->verts[va].sT0 - voodoo->verts[vb].sT0) * dyBC - (voodoo->verts[vb].sT0 - voodoo->verts[vc].sT0) * dyAB) * 4294967296.0f); voodoo->params.tmu[0].dTdY = (int64_t)(((voodoo->verts[vb].sT0 - voodoo->verts[vc].sT0) * dxAB - (voodoo->verts[va].sT0 - voodoo->verts[vb].sT0) * dxBC) * 4294967296.0f); voodoo->params.tmu[1].startS = voodoo->params.tmu[0].startS; voodoo->params.tmu[1].dSdX = voodoo->params.tmu[0].dSdX; voodoo->params.tmu[1].dSdY = voodoo->params.tmu[0].dSdY; voodoo->params.tmu[1].startT = voodoo->params.tmu[0].startT; voodoo->params.tmu[1].dTdX = voodoo->params.tmu[0].dTdX; voodoo->params.tmu[1].dTdY = voodoo->params.tmu[0].dTdY; } if (voodoo->sSetupMode & SETUPMODE_W1) { voodoo->params.tmu[1].startW = (int64_t)(voodoo->verts[va].sW1 * 4294967296.0f); voodoo->params.tmu[1].dWdX = (int64_t)(((voodoo->verts[va].sW1 - voodoo->verts[vb].sW1) * dyBC - (voodoo->verts[vb].sW1 - voodoo->verts[vc].sW1) * dyAB) * 4294967296.0f); voodoo->params.tmu[1].dWdY = (int64_t)(((voodoo->verts[vb].sW1 - voodoo->verts[vc].sW1) * dxAB - (voodoo->verts[va].sW1 - voodoo->verts[vb].sW1) * dxBC) * 4294967296.0f); } if (voodoo->sSetupMode & SETUPMODE_S1_T1) { voodoo->params.tmu[1].startS = (int64_t)(voodoo->verts[va].sS1 * 4294967296.0f); voodoo->params.tmu[1].dSdX = (int64_t)(((voodoo->verts[va].sS1 - voodoo->verts[vb].sS1) * dyBC - (voodoo->verts[vb].sS1 - voodoo->verts[vc].sS1) * dyAB) * 4294967296.0f); voodoo->params.tmu[1].dSdY = (int64_t)(((voodoo->verts[vb].sS1 - voodoo->verts[vc].sS1) * dxAB - (voodoo->verts[va].sS1 - voodoo->verts[vb].sS1) * dxBC) * 4294967296.0f); voodoo->params.tmu[1].startT = (int64_t)(voodoo->verts[va].sT1 * 4294967296.0f); voodoo->params.tmu[1].dTdX = (int64_t)(((voodoo->verts[va].sT1 - voodoo->verts[vb].sT1) * dyBC - (voodoo->verts[vb].sT1 - voodoo->verts[vc].sT1) * dyAB) * 4294967296.0f); voodoo->params.tmu[1].dTdY = (int64_t)(((voodoo->verts[vb].sT1 - voodoo->verts[vc].sT1) * dxAB - (voodoo->verts[va].sT1 - voodoo->verts[vb].sT1) * dxBC) * 4294967296.0f); } voodoo->params.sign = (area < 0.0); if (voodoo->ncc_dirty[0]) voodoo_update_ncc(voodoo, 0); if (voodoo->ncc_dirty[1]) voodoo_update_ncc(voodoo, 1); voodoo->ncc_dirty[0] = voodoo->ncc_dirty[1] = 0; queue_triangle(voodoo, &voodoo->params); } enum { BLIT_COMMAND_SCREEN_TO_SCREEN = 0, BLIT_COMMAND_CPU_TO_SCREEN = 1, BLIT_COMMAND_RECT_FILL = 2, BLIT_COMMAND_SGRAM_FILL = 3 }; enum { BLIT_SRC_1BPP = (0 << 3), BLIT_SRC_1BPP_BYTE_PACKED = (1 << 3), BLIT_SRC_16BPP = (2 << 3), BLIT_SRC_24BPP = (3 << 3), BLIT_SRC_24BPP_DITHER_2X2 = (4 << 3), BLIT_SRC_24BPP_DITHER_4X4 = (5 << 3) }; enum { BLIT_SRC_RGB_ARGB = (0 << 6), BLIT_SRC_RGB_ABGR = (1 << 6), BLIT_SRC_RGB_RGBA = (2 << 6), BLIT_SRC_RGB_BGRA = (3 << 6) }; enum { BLIT_COMMAND_MASK = 7, BLIT_SRC_FORMAT = (7 << 3), BLIT_SRC_RGB_FORMAT = (3 << 6), BLIT_SRC_CHROMA = (1 << 10), BLIT_DST_CHROMA = (1 << 12), BLIT_CLIPPING_ENABLED = (1 << 16) }; enum { BLIT_ROP_DST_PASS = (1 << 0), BLIT_ROP_SRC_PASS = (1 << 1) }; #define MIX(src_dat, dst_dat, rop) \ switch (rop) \ { \ case 0x0: dst_dat = 0; break; \ case 0x1: dst_dat = ~(src_dat | dst_dat); break; \ case 0x2: dst_dat = ~src_dat & dst_dat; break; \ case 0x3: dst_dat = ~src_dat; break; \ case 0x4: dst_dat = src_dat & ~dst_dat; break; \ case 0x5: dst_dat = ~dst_dat; break; \ case 0x6: dst_dat = src_dat ^ dst_dat; break; \ case 0x7: dst_dat = ~(src_dat & dst_dat); break; \ case 0x8: dst_dat = src_dat & dst_dat; break; \ case 0x9: dst_dat = ~(src_dat ^ dst_dat); break; \ case 0xa: dst_dat = dst_dat; break; \ case 0xb: dst_dat = ~src_dat | dst_dat; break; \ case 0xc: dst_dat = src_dat; break; \ case 0xd: dst_dat = src_dat | ~dst_dat; break; \ case 0xe: dst_dat = src_dat | dst_dat; break; \ case 0xf: dst_dat = 0xffff; break; \ } #define ABS(x) ((x) > 0 ? (x) : -(x)) static void blit_start(voodoo_t *voodoo) { uint64_t dat64; int size_x = ABS(voodoo->bltSizeX), size_y = ABS(voodoo->bltSizeY); int x_dir = (voodoo->bltSizeX > 0) ? 1 : -1; int y_dir = (voodoo->bltSizeY > 0) ? 1 : -1; int src_x, dst_x; int src_y = voodoo->bltSrcY & 0x7ff, dst_y = voodoo->bltDstY & 0x7ff; int src_stride = (voodoo->bltCommand & BLTCMD_SRC_TILED) ? ((voodoo->bltSrcXYStride & 0x3f) * 32*2) : (voodoo->bltSrcXYStride & 0xff8); int dst_stride = (voodoo->bltCommand & BLTCMD_DST_TILED) ? ((voodoo->bltDstXYStride & 0x3f) * 32*2) : (voodoo->bltDstXYStride & 0xff8); uint32_t src_base_addr = (voodoo->bltCommand & BLTCMD_SRC_TILED) ? ((voodoo->bltSrcBaseAddr & 0x3ff) << 12) : (voodoo->bltSrcBaseAddr & 0x3ffff8); uint32_t dst_base_addr = (voodoo->bltCommand & BLTCMD_DST_TILED) ? ((voodoo->bltDstBaseAddr & 0x3ff) << 12) : (voodoo->bltDstBaseAddr & 0x3ffff8); int x, y; /* pclog("blit_start: command=%08x srcX=%i srcY=%i dstX=%i dstY=%i sizeX=%i sizeY=%i color=%04x,%04x\n", voodoo->bltCommand, voodoo->bltSrcX, voodoo->bltSrcY, voodoo->bltDstX, voodoo->bltDstY, voodoo->bltSizeX, voodoo->bltSizeY, voodoo->bltColorFg, voodoo->bltColorBg);*/ wait_for_render_thread_idle(voodoo); switch (voodoo->bltCommand & BLIT_COMMAND_MASK) { case BLIT_COMMAND_SCREEN_TO_SCREEN: for (y = 0; y <= size_y; y++) { uint16_t *src = (uint16_t *)&voodoo->fb_mem[src_base_addr + src_y*src_stride]; uint16_t *dst = (uint16_t *)&voodoo->fb_mem[dst_base_addr + dst_y*dst_stride]; int src_x = voodoo->bltSrcX, dst_x = voodoo->bltDstX; for (x = 0; x <= size_x; x++) { uint16_t src_dat = src[src_x]; uint16_t dst_dat = dst[dst_x]; int rop = 0; if (voodoo->bltCommand & BLIT_CLIPPING_ENABLED) { if (dst_x < voodoo->bltClipLeft || dst_x > voodoo->bltClipRight || dst_y < voodoo->bltClipLowY || dst_y > voodoo->bltClipHighY) goto skip_pixel_blit; } if (voodoo->bltCommand & BLIT_SRC_CHROMA) { int r = (src_dat >> 11); int g = (src_dat >> 5) & 0x3f; int b = src_dat & 0x1f; if (r >= voodoo->bltSrcChromaMinR && r <= voodoo->bltSrcChromaMaxR && g >= voodoo->bltSrcChromaMinG && g <= voodoo->bltSrcChromaMaxG && b >= voodoo->bltSrcChromaMinB && b <= voodoo->bltSrcChromaMaxB) rop |= BLIT_ROP_SRC_PASS; } if (voodoo->bltCommand & BLIT_DST_CHROMA) { int r = (dst_dat >> 11); int g = (dst_dat >> 5) & 0x3f; int b = dst_dat & 0x1f; if (r >= voodoo->bltDstChromaMinR && r <= voodoo->bltDstChromaMaxR && g >= voodoo->bltDstChromaMinG && g <= voodoo->bltDstChromaMaxG && b >= voodoo->bltDstChromaMinB && b <= voodoo->bltDstChromaMaxB) rop |= BLIT_ROP_DST_PASS; } MIX(src_dat, dst_dat, voodoo->bltRop[rop]); dst[dst_x] = dst_dat; skip_pixel_blit: src_x += x_dir; dst_x += x_dir; } src_y += y_dir; dst_y += y_dir; } break; case BLIT_COMMAND_CPU_TO_SCREEN: voodoo->blt.dst_x = voodoo->bltDstX; voodoo->blt.dst_y = voodoo->bltDstY; voodoo->blt.cur_x = 0; voodoo->blt.size_x = size_x; voodoo->blt.size_y = size_y; voodoo->blt.x_dir = x_dir; voodoo->blt.y_dir = y_dir; voodoo->blt.dst_stride = (voodoo->bltCommand & BLTCMD_DST_TILED) ? ((voodoo->bltDstXYStride & 0x3f) * 32*2) : (voodoo->bltDstXYStride & 0xff8); break; case BLIT_COMMAND_RECT_FILL: for (y = 0; y <= size_y; y++) { uint16_t *dst = (uint16_t *)&voodoo->fb_mem[dst_base_addr + dst_y*dst_stride]; int dst_x = voodoo->bltDstX; for (x = 0; x <= size_x; x++) { if (voodoo->bltCommand & BLIT_CLIPPING_ENABLED) { if (dst_x < voodoo->bltClipLeft || dst_x > voodoo->bltClipRight || dst_y < voodoo->bltClipLowY || dst_y > voodoo->bltClipHighY) goto skip_pixel_fill; } dst[dst_x] = voodoo->bltColorFg; skip_pixel_fill: dst_x += x_dir; } dst_y += y_dir; } break; case BLIT_COMMAND_SGRAM_FILL: /*32x32 tiles - 2kb*/ dst_y = voodoo->bltDstY & 0x3ff; size_x = voodoo->bltSizeX & 0x1ff; //512*8 = 4kb size_y = voodoo->bltSizeY & 0x3ff; dat64 = voodoo->bltColorFg | ((uint64_t)voodoo->bltColorFg << 16) | ((uint64_t)voodoo->bltColorFg << 32) | ((uint64_t)voodoo->bltColorFg << 48); for (y = 0; y <= size_y; y++) { uint64_t *dst; /*This may be wrong*/ if (!y) { dst_x = voodoo->bltDstX & 0x1ff; size_x = 511 - dst_x; } else if (y < size_y) { dst_x = 0; size_x = 511; } else { dst_x = 0; size_x = voodoo->bltSizeX & 0x1ff; } dst = (uint64_t *)&voodoo->fb_mem[(dst_y*512*8 + dst_x*8) & voodoo->fb_mask]; for (x = 0; x <= size_x; x++) dst[x] = dat64; dst_y++; } break; default: fatal("bad blit command %08x\n", voodoo->bltCommand); } } static void blit_data(voodoo_t *voodoo, uint32_t data) { uint32_t data_orig = data; int src_bits = 32; uint32_t base_addr = (voodoo->bltCommand & BLTCMD_DST_TILED) ? ((voodoo->bltDstBaseAddr & 0x3ff) << 12) : (voodoo->bltDstBaseAddr & 0x3ffff8); uint16_t *dst = (uint16_t *)&voodoo->fb_mem[base_addr + voodoo->blt.dst_y*voodoo->blt.dst_stride]; if ((voodoo->bltCommand & BLIT_COMMAND_MASK) != BLIT_COMMAND_CPU_TO_SCREEN) return; while (src_bits && voodoo->blt.cur_x <= voodoo->blt.size_x) { int r, g, b; uint16_t src_dat, dst_dat; int x = (voodoo->blt.x_dir > 0) ? (voodoo->blt.dst_x + voodoo->blt.cur_x) : (voodoo->blt.dst_x - voodoo->blt.cur_x); int rop = 0; switch (voodoo->bltCommand & BLIT_SRC_FORMAT) { case BLIT_SRC_1BPP: case BLIT_SRC_1BPP_BYTE_PACKED: src_dat = (data & 1) ? voodoo->bltColorFg : voodoo->bltColorBg; data >>= 1; src_bits--; break; case BLIT_SRC_16BPP: switch (voodoo->bltCommand & BLIT_SRC_RGB_FORMAT) { case BLIT_SRC_RGB_ARGB: case BLIT_SRC_RGB_RGBA: src_dat = data & 0xffff; break; case BLIT_SRC_RGB_ABGR: case BLIT_SRC_RGB_BGRA: src_dat = ((data & 0xf800) >> 11) | (data & 0x07c0) | ((data & 0x0038) << 11); break; } data >>= 16; src_bits -= 16; break; case BLIT_SRC_24BPP: case BLIT_SRC_24BPP_DITHER_2X2: case BLIT_SRC_24BPP_DITHER_4X4: switch (voodoo->bltCommand & BLIT_SRC_RGB_FORMAT) { case BLIT_SRC_RGB_ARGB: r = (data >> 16) & 0xff; g = (data >> 8) & 0xff; b = data & 0xff; break; case BLIT_SRC_RGB_ABGR: r = data & 0xff; g = (data >> 8) & 0xff; b = (data >> 16) & 0xff; break; case BLIT_SRC_RGB_RGBA: r = (data >> 24) & 0xff; g = (data >> 16) & 0xff; b = (data >> 8) & 0xff; break; case BLIT_SRC_RGB_BGRA: r = (data >> 8) & 0xff; g = (data >> 16) & 0xff; b = (data >> 24) & 0xff; break; } switch (voodoo->bltCommand & BLIT_SRC_FORMAT) { case BLIT_SRC_24BPP: src_dat = (b >> 3) | ((g & 0xfc) << 3) | ((r & 0xf8) << 8); break; case BLIT_SRC_24BPP_DITHER_2X2: r = dither_rb2x2[r][voodoo->blt.dst_y & 1][x & 1]; g = dither_g2x2[g][voodoo->blt.dst_y & 1][x & 1]; b = dither_rb2x2[b][voodoo->blt.dst_y & 1][x & 1]; src_dat = (b >> 3) | ((g & 0xfc) << 3) | ((r & 0xf8) << 8); break; case BLIT_SRC_24BPP_DITHER_4X4: r = dither_rb[r][voodoo->blt.dst_y & 3][x & 3]; g = dither_g[g][voodoo->blt.dst_y & 3][x & 3]; b = dither_rb[b][voodoo->blt.dst_y & 3][x & 3]; src_dat = (b >> 3) | ((g & 0xfc) << 3) | ((r & 0xf8) << 8); break; } src_bits = 0; break; } if (voodoo->bltCommand & BLIT_CLIPPING_ENABLED) { if (x < voodoo->bltClipLeft || x > voodoo->bltClipRight || voodoo->blt.dst_y < voodoo->bltClipLowY || voodoo->blt.dst_y > voodoo->bltClipHighY) goto skip_pixel; } dst_dat = dst[x]; if (voodoo->bltCommand & BLIT_SRC_CHROMA) { r = (src_dat >> 11); g = (src_dat >> 5) & 0x3f; b = src_dat & 0x1f; if (r >= voodoo->bltSrcChromaMinR && r <= voodoo->bltSrcChromaMaxR && g >= voodoo->bltSrcChromaMinG && g <= voodoo->bltSrcChromaMaxG && b >= voodoo->bltSrcChromaMinB && b <= voodoo->bltSrcChromaMaxB) rop |= BLIT_ROP_SRC_PASS; } if (voodoo->bltCommand & BLIT_DST_CHROMA) { r = (dst_dat >> 11); g = (dst_dat >> 5) & 0x3f; b = dst_dat & 0x1f; if (r >= voodoo->bltDstChromaMinR && r <= voodoo->bltDstChromaMaxR && g >= voodoo->bltDstChromaMinG && g <= voodoo->bltDstChromaMaxG && b >= voodoo->bltDstChromaMinB && b <= voodoo->bltDstChromaMaxB) rop |= BLIT_ROP_DST_PASS; } MIX(src_dat, dst_dat, voodoo->bltRop[rop]); dst[x] = dst_dat; skip_pixel: voodoo->blt.cur_x++; } if (voodoo->blt.cur_x > voodoo->blt.size_x) { voodoo->blt.size_y--; if (voodoo->blt.size_y >= 0) { voodoo->blt.cur_x = 0; voodoo->blt.dst_y += voodoo->blt.y_dir; } } } enum { CHIP_FBI = 0x1, CHIP_TREX0 = 0x2, CHIP_TREX1 = 0x4, CHIP_TREX2 = 0x8 }; static void wait_for_swap_complete(voodoo_t *voodoo) { while (voodoo->swap_pending) { thread_wait_event(voodoo->wake_fifo_thread, -1); thread_reset_event(voodoo->wake_fifo_thread); if ((voodoo->swap_pending && voodoo->flush) || FIFO_ENTRIES == 65536) { /*Main thread is waiting for FIFO to empty, so skip vsync wait and just swap*/ memset(voodoo->dirty_line, 1, 1024); voodoo->front_offset = voodoo->params.front_offset; voodoo->swap_count--; voodoo->swap_pending = 0; break; } } } static void voodoo_reg_writel(uint32_t addr, uint32_t val, void *p) { voodoo_t *voodoo = (voodoo_t *)p; union { uint32_t i; float f; } tempif; int ad21 = addr & (1 << 21); int chip = (addr >> 10) & 0xf; if (!chip) chip = 0xf; tempif.i = val; //pclog("voodoo_reg_write_l: addr=%08x val=%08x(%f) chip=%x\n", addr, val, tempif.f, chip); addr &= 0x3fc; if ((voodoo->fbiInit3 & FBIINIT3_REMAP) && addr < 0x100 && ad21) addr |= 0x400; switch (addr) { case SST_swapbufferCMD: // pclog(" start swap buffer command\n"); if (TRIPLE_BUFFER) { voodoo->disp_buffer = (voodoo->disp_buffer + 1) % 3; voodoo->draw_buffer = (voodoo->draw_buffer + 1) % 3; } else { voodoo->disp_buffer = !voodoo->disp_buffer; voodoo->draw_buffer = !voodoo->draw_buffer; } voodoo_recalc(voodoo); voodoo->params.swapbufferCMD = val; pclog("Swap buffer %08x %d %p\n", val, voodoo->swap_count, &voodoo->swap_count); // voodoo->front_offset = params->front_offset; wait_for_render_thread_idle(voodoo); if (!(val & 1)) { memset(voodoo->dirty_line, 1, 1024); voodoo->front_offset = voodoo->params.front_offset; voodoo->swap_count--; } else if (TRIPLE_BUFFER) { if (voodoo->swap_pending) wait_for_swap_complete(voodoo); voodoo->swap_interval = (val >> 1) & 0xff; voodoo->swap_offset = voodoo->params.front_offset; voodoo->swap_pending = 1; } else { voodoo->swap_interval = (val >> 1) & 0xff; voodoo->swap_offset = voodoo->params.front_offset; voodoo->swap_pending = 1; wait_for_swap_complete(voodoo); } voodoo->cmd_read++; break; case SST_vertexAx: case SST_remap_vertexAx: voodoo->params.vertexAx = val & 0xffff; break; case SST_vertexAy: case SST_remap_vertexAy: voodoo->params.vertexAy = val & 0xffff; break; case SST_vertexBx: case SST_remap_vertexBx: voodoo->params.vertexBx = val & 0xffff; break; case SST_vertexBy: case SST_remap_vertexBy: voodoo->params.vertexBy = val & 0xffff; break; case SST_vertexCx: case SST_remap_vertexCx: voodoo->params.vertexCx = val & 0xffff; break; case SST_vertexCy: case SST_remap_vertexCy: voodoo->params.vertexCy = val & 0xffff; break; case SST_startR: case SST_remap_startR: voodoo->params.startR = val & 0xffffff; break; case SST_startG: case SST_remap_startG: voodoo->params.startG = val & 0xffffff; break; case SST_startB: case SST_remap_startB: voodoo->params.startB = val & 0xffffff; break; case SST_startZ: case SST_remap_startZ: voodoo->params.startZ = val; break; case SST_startA: case SST_remap_startA: voodoo->params.startA = val & 0xffffff; break; case SST_startS: case SST_remap_startS: if (chip & CHIP_TREX0) voodoo->params.tmu[0].startS = ((int64_t)(int32_t)val) << 14; if (chip & CHIP_TREX1) voodoo->params.tmu[1].startS = ((int64_t)(int32_t)val) << 14; break; case SST_startT: case SST_remap_startT: if (chip & CHIP_TREX0) voodoo->params.tmu[0].startT = ((int64_t)(int32_t)val) << 14; if (chip & CHIP_TREX1) voodoo->params.tmu[1].startT = ((int64_t)(int32_t)val) << 14; break; case SST_startW: case SST_remap_startW: if (chip & CHIP_FBI) voodoo->params.startW = (int64_t)(int32_t)val << 2; if (chip & CHIP_TREX0) voodoo->params.tmu[0].startW = (int64_t)(int32_t)val << 2; if (chip & CHIP_TREX1) voodoo->params.tmu[1].startW = (int64_t)(int32_t)val << 2; break; case SST_dRdX: case SST_remap_dRdX: voodoo->params.dRdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0); break; case SST_dGdX: case SST_remap_dGdX: voodoo->params.dGdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0); break; case SST_dBdX: case SST_remap_dBdX: voodoo->params.dBdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0); break; case SST_dZdX: case SST_remap_dZdX: voodoo->params.dZdX = val; break; case SST_dAdX: case SST_remap_dAdX: voodoo->params.dAdX = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0); break; case SST_dSdX: case SST_remap_dSdX: if (chip & CHIP_TREX0) voodoo->params.tmu[0].dSdX = ((int64_t)(int32_t)val) << 14; if (chip & CHIP_TREX1) voodoo->params.tmu[1].dSdX = ((int64_t)(int32_t)val) << 14; break; case SST_dTdX: case SST_remap_dTdX: if (chip & CHIP_TREX0) voodoo->params.tmu[0].dTdX = ((int64_t)(int32_t)val) << 14; if (chip & CHIP_TREX1) voodoo->params.tmu[1].dTdX = ((int64_t)(int32_t)val) << 14; break; case SST_dWdX: case SST_remap_dWdX: if (chip & CHIP_TREX0) voodoo->params.tmu[0].dWdX = (int64_t)(int32_t)val << 2; if (chip & CHIP_TREX1) voodoo->params.tmu[1].dWdX = (int64_t)(int32_t)val << 2; if (chip & CHIP_FBI) voodoo->params.dWdX = (int64_t)(int32_t)val << 2; break; case SST_dRdY: case SST_remap_dRdY: voodoo->params.dRdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0); break; case SST_dGdY: case SST_remap_dGdY: voodoo->params.dGdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0); break; case SST_dBdY: case SST_remap_dBdY: voodoo->params.dBdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0); break; case SST_dZdY: case SST_remap_dZdY: voodoo->params.dZdY = val; break; case SST_dAdY: case SST_remap_dAdY: voodoo->params.dAdY = (val & 0xffffff) | ((val & 0x800000) ? 0xff000000 : 0); break; case SST_dSdY: case SST_remap_dSdY: if (chip & CHIP_TREX0) voodoo->params.tmu[0].dSdY = ((int64_t)(int32_t)val) << 14; if (chip & CHIP_TREX1) voodoo->params.tmu[1].dSdY = ((int64_t)(int32_t)val) << 14; break; case SST_dTdY: case SST_remap_dTdY: if (chip & CHIP_TREX0) voodoo->params.tmu[0].dTdY = ((int64_t)(int32_t)val) << 14; if (chip & CHIP_TREX1) voodoo->params.tmu[1].dTdY = ((int64_t)(int32_t)val) << 14; break; case SST_dWdY: case SST_remap_dWdY: if (chip & CHIP_TREX0) voodoo->params.tmu[0].dWdY = (int64_t)(int32_t)val << 2; if (chip & CHIP_TREX1) voodoo->params.tmu[1].dWdY = (int64_t)(int32_t)val << 2; if (chip & CHIP_FBI) voodoo->params.dWdY = (int64_t)(int32_t)val << 2; break; case SST_triangleCMD: case SST_remap_triangleCMD: voodoo->params.sign = val & (1 << 31); if (voodoo->ncc_dirty[0]) voodoo_update_ncc(voodoo, 0); if (voodoo->ncc_dirty[1]) voodoo_update_ncc(voodoo, 1); voodoo->ncc_dirty[0] = voodoo->ncc_dirty[1] = 0; queue_triangle(voodoo, &voodoo->params); voodoo->cmd_read++; break; case SST_fvertexAx: case SST_remap_fvertexAx: voodoo->fvertexAx.i = val; voodoo->params.vertexAx = (int32_t)(int16_t)(int32_t)(voodoo->fvertexAx.f * 16.0f) & 0xffff; break; case SST_fvertexAy: case SST_remap_fvertexAy: voodoo->fvertexAy.i = val; voodoo->params.vertexAy = (int32_t)(int16_t)(int32_t)(voodoo->fvertexAy.f * 16.0f) & 0xffff; break; case SST_fvertexBx: case SST_remap_fvertexBx: voodoo->fvertexBx.i = val; voodoo->params.vertexBx = (int32_t)(int16_t)(int32_t)(voodoo->fvertexBx.f * 16.0f) & 0xffff; break; case SST_fvertexBy: case SST_remap_fvertexBy: voodoo->fvertexBy.i = val; voodoo->params.vertexBy = (int32_t)(int16_t)(int32_t)(voodoo->fvertexBy.f * 16.0f) & 0xffff; break; case SST_fvertexCx: case SST_remap_fvertexCx: voodoo->fvertexCx.i = val; voodoo->params.vertexCx = (int32_t)(int16_t)(int32_t)(voodoo->fvertexCx.f * 16.0f) & 0xffff; break; case SST_fvertexCy: case SST_remap_fvertexCy: voodoo->fvertexCy.i = val; voodoo->params.vertexCy = (int32_t)(int16_t)(int32_t)(voodoo->fvertexCy.f * 16.0f) & 0xffff; break; case SST_fstartR: case SST_remap_fstartR: tempif.i = val; voodoo->params.startR = (int32_t)(tempif.f * 4096.0f); break; case SST_fstartG: case SST_remap_fstartG: tempif.i = val; voodoo->params.startG = (int32_t)(tempif.f * 4096.0f); break; case SST_fstartB: case SST_remap_fstartB: tempif.i = val; voodoo->params.startB = (int32_t)(tempif.f * 4096.0f); break; case SST_fstartZ: case SST_remap_fstartZ: tempif.i = val; voodoo->params.startZ = (int32_t)(tempif.f * 4096.0f); break; case SST_fstartA: case SST_remap_fstartA: tempif.i = val; voodoo->params.startA = (int32_t)(tempif.f * 4096.0f); break; case SST_fstartS: case SST_remap_fstartS: tempif.i = val; if (chip & CHIP_TREX0) voodoo->params.tmu[0].startS = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_TREX1) voodoo->params.tmu[1].startS = (int64_t)(tempif.f * 4294967296.0f); break; case SST_fstartT: case SST_remap_fstartT: tempif.i = val; if (chip & CHIP_TREX0) voodoo->params.tmu[0].startT = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_TREX1) voodoo->params.tmu[1].startT = (int64_t)(tempif.f * 4294967296.0f); break; case SST_fstartW: case SST_remap_fstartW: tempif.i = val; if (chip & CHIP_TREX0) voodoo->params.tmu[0].startW = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_TREX1) voodoo->params.tmu[1].startW = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_FBI) voodoo->params.startW = (int64_t)(tempif.f * 4294967296.0f); break; case SST_fdRdX: case SST_remap_fdRdX: tempif.i = val; voodoo->params.dRdX = (int32_t)(tempif.f * 4096.0f); break; case SST_fdGdX: case SST_remap_fdGdX: tempif.i = val; voodoo->params.dGdX = (int32_t)(tempif.f * 4096.0f); break; case SST_fdBdX: case SST_remap_fdBdX: tempif.i = val; voodoo->params.dBdX = (int32_t)(tempif.f * 4096.0f); break; case SST_fdZdX: case SST_remap_fdZdX: tempif.i = val; voodoo->params.dZdX = (int32_t)(tempif.f * 4096.0f); break; case SST_fdAdX: case SST_remap_fdAdX: tempif.i = val; voodoo->params.dAdX = (int32_t)(tempif.f * 4096.0f); break; case SST_fdSdX: case SST_remap_fdSdX: tempif.i = val; if (chip & CHIP_TREX0) voodoo->params.tmu[0].dSdX = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_TREX1) voodoo->params.tmu[1].dSdX = (int64_t)(tempif.f * 4294967296.0f); break; case SST_fdTdX: case SST_remap_fdTdX: tempif.i = val; if (chip & CHIP_TREX0) voodoo->params.tmu[0].dTdX = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_TREX1) voodoo->params.tmu[1].dTdX = (int64_t)(tempif.f * 4294967296.0f); break; case SST_fdWdX: case SST_remap_fdWdX: tempif.i = val; if (chip & CHIP_TREX0) voodoo->params.tmu[0].dWdX = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_TREX1) voodoo->params.tmu[1].dWdX = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_FBI) voodoo->params.dWdX = (int64_t)(tempif.f * 4294967296.0f); break; case SST_fdRdY: case SST_remap_fdRdY: tempif.i = val; voodoo->params.dRdY = (int32_t)(tempif.f * 4096.0f); break; case SST_fdGdY: case SST_remap_fdGdY: tempif.i = val; voodoo->params.dGdY = (int32_t)(tempif.f * 4096.0f); break; case SST_fdBdY: case SST_remap_fdBdY: tempif.i = val; voodoo->params.dBdY = (int32_t)(tempif.f * 4096.0f); break; case SST_fdZdY: case SST_remap_fdZdY: tempif.i = val; voodoo->params.dZdY = (int32_t)(tempif.f * 4096.0f); break; case SST_fdAdY: case SST_remap_fdAdY: tempif.i = val; voodoo->params.dAdY = (int32_t)(tempif.f * 4096.0f); break; case SST_fdSdY: case SST_remap_fdSdY: tempif.i = val; if (chip & CHIP_TREX0) voodoo->params.tmu[0].dSdY = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_TREX1) voodoo->params.tmu[1].dSdY = (int64_t)(tempif.f * 4294967296.0f); break; case SST_fdTdY: case SST_remap_fdTdY: tempif.i = val; if (chip & CHIP_TREX0) voodoo->params.tmu[0].dTdY = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_TREX1) voodoo->params.tmu[1].dTdY = (int64_t)(tempif.f * 4294967296.0f); break; case SST_fdWdY: case SST_remap_fdWdY: tempif.i = val; if (chip & CHIP_TREX0) voodoo->params.tmu[0].dWdY = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_TREX1) voodoo->params.tmu[1].dWdY = (int64_t)(tempif.f * 4294967296.0f); if (chip & CHIP_FBI) voodoo->params.dWdY = (int64_t)(tempif.f * 4294967296.0f); break; case SST_ftriangleCMD: voodoo->params.sign = val & (1 << 31); if (voodoo->ncc_dirty[0]) voodoo_update_ncc(voodoo, 0); if (voodoo->ncc_dirty[1]) voodoo_update_ncc(voodoo, 1); voodoo->ncc_dirty[0] = voodoo->ncc_dirty[1] = 0; queue_triangle(voodoo, &voodoo->params); voodoo->cmd_read++; break; case SST_fbzColorPath: voodoo->params.fbzColorPath = val; voodoo->rgb_sel = val & 3; break; case SST_fogMode: voodoo->params.fogMode = val; break; case SST_alphaMode: voodoo->params.alphaMode = val; break; case SST_fbzMode: voodoo->params.fbzMode = val; voodoo_recalc(voodoo); break; case SST_lfbMode: voodoo->lfbMode = val; voodoo_recalc(voodoo); break; case SST_clipLeftRight: if (voodoo->type >= VOODOO_2) { voodoo->params.clipRight = val & 0xfff; voodoo->params.clipLeft = (val >> 16) & 0xfff; } else { voodoo->params.clipRight = val & 0x3ff; voodoo->params.clipLeft = (val >> 16) & 0x3ff; } break; case SST_clipLowYHighY: if (voodoo->type >= VOODOO_2) { voodoo->params.clipHighY = val & 0xfff; voodoo->params.clipLowY = (val >> 16) & 0xfff; } else { voodoo->params.clipHighY = val & 0x3ff; voodoo->params.clipLowY = (val >> 16) & 0x3ff; } break; case SST_nopCMD: voodoo->cmd_read++; voodoo->fbiPixelsIn = 0; voodoo->fbiChromaFail = 0; voodoo->fbiZFuncFail = 0; voodoo->fbiAFuncFail = 0; voodoo->fbiPixelsOut = 0; break; case SST_fastfillCMD: wait_for_render_thread_idle(voodoo); voodoo_fastfill(voodoo, &voodoo->params); voodoo->cmd_read++; break; case SST_fogColor: voodoo->params.fogColor.r = (val >> 16) & 0xff; voodoo->params.fogColor.g = (val >> 8) & 0xff; voodoo->params.fogColor.b = val & 0xff; break; case SST_zaColor: voodoo->params.zaColor = val; break; case SST_chromaKey: voodoo->params.chromaKey_r = (val >> 16) & 0xff; voodoo->params.chromaKey_g = (val >> 8) & 0xff; voodoo->params.chromaKey_b = val & 0xff; voodoo->params.chromaKey = val & 0xffffff; break; case SST_stipple: voodoo->params.stipple = val; break; case SST_color0: voodoo->params.color0 = val; break; case SST_color1: voodoo->params.color1 = val; break; case SST_fogTable00: case SST_fogTable01: case SST_fogTable02: case SST_fogTable03: case SST_fogTable04: case SST_fogTable05: case SST_fogTable06: case SST_fogTable07: case SST_fogTable08: case SST_fogTable09: case SST_fogTable0a: case SST_fogTable0b: case SST_fogTable0c: case SST_fogTable0d: case SST_fogTable0e: case SST_fogTable0f: case SST_fogTable10: case SST_fogTable11: case SST_fogTable12: case SST_fogTable13: case SST_fogTable14: case SST_fogTable15: case SST_fogTable16: case SST_fogTable17: case SST_fogTable18: case SST_fogTable19: case SST_fogTable1a: case SST_fogTable1b: case SST_fogTable1c: case SST_fogTable1d: case SST_fogTable1e: case SST_fogTable1f: addr = (addr - SST_fogTable00) >> 1; voodoo->params.fogTable[addr].dfog = val & 0xff; voodoo->params.fogTable[addr].fog = (val >> 8) & 0xff; voodoo->params.fogTable[addr+1].dfog = (val >> 16) & 0xff; voodoo->params.fogTable[addr+1].fog = (val >> 24) & 0xff; 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_sSetupMode: voodoo->sSetupMode = val; break; case SST_sVx: tempif.i = val; voodoo->verts[3].sVx = tempif.f; // pclog("sVx[%i]=%f\n", voodoo->vertex_num, tempif.f); break; case SST_sVy: tempif.i = val; voodoo->verts[3].sVy = tempif.f; // pclog("sVy[%i]=%f\n", voodoo->vertex_num, tempif.f); break; case SST_sARGB: voodoo->verts[3].sBlue = (float)(val & 0xff); voodoo->verts[3].sGreen = (float)((val >> 8) & 0xff); voodoo->verts[3].sRed = (float)((val >> 16) & 0xff); voodoo->verts[3].sAlpha = (float)((val >> 24) & 0xff); break; case SST_sRed: tempif.i = val; voodoo->verts[3].sRed = tempif.f; break; case SST_sGreen: tempif.i = val; voodoo->verts[3].sGreen = tempif.f; break; case SST_sBlue: tempif.i = val; voodoo->verts[3].sBlue = tempif.f; break; case SST_sAlpha: tempif.i = val; voodoo->verts[3].sAlpha = tempif.f; break; case SST_sVz: tempif.i = val; voodoo->verts[3].sVz = tempif.f; break; case SST_sWb: tempif.i = val; voodoo->verts[3].sWb = tempif.f; break; case SST_sW0: tempif.i = val; voodoo->verts[3].sW0 = tempif.f; break; case SST_sS0: tempif.i = val; voodoo->verts[3].sS0 = tempif.f; break; case SST_sT0: tempif.i = val; voodoo->verts[3].sT0 = tempif.f; break; case SST_sW1: tempif.i = val; voodoo->verts[3].sW1 = tempif.f; break; case SST_sS1: tempif.i = val; voodoo->verts[3].sS1 = tempif.f; break; case SST_sT1: tempif.i = val; voodoo->verts[3].sT1 = tempif.f; break; case SST_sBeginTriCMD: // pclog("sBeginTriCMD %i %f\n", voodoo->vertex_num, voodoo->verts[4].sVx); voodoo->verts[0] = voodoo->verts[3]; voodoo->vertex_num = 1; voodoo->num_verticies = 1; break; case SST_sDrawTriCMD: // pclog("sDrawTriCMD %i %i %i\n", voodoo->num_verticies, voodoo->vertex_num, voodoo->sSetupMode & SETUPMODE_STRIP_MODE); if (voodoo->vertex_num == 3) voodoo->vertex_num = (voodoo->sSetupMode & SETUPMODE_STRIP_MODE) ? 1 : 0; voodoo->verts[voodoo->vertex_num] = voodoo->verts[3]; voodoo->num_verticies++; voodoo->vertex_num++; if (voodoo->num_verticies == 3) { // pclog("triangle_setup\n"); triangle_setup(voodoo); voodoo->num_verticies = 2; } if (voodoo->vertex_num == 4) fatal("sDrawTriCMD overflow\n"); break; case SST_bltSrcBaseAddr: voodoo->bltSrcBaseAddr = val & 0x3fffff; break; case SST_bltDstBaseAddr: // pclog("Write bltDstBaseAddr %08x\n", val); voodoo->bltDstBaseAddr = val & 0x3fffff; break; case SST_bltXYStrides: voodoo->bltSrcXYStride = val & 0xfff; voodoo->bltDstXYStride = (val >> 16) & 0xfff; // pclog("Write bltXYStrides %08x\n", val); break; case SST_bltSrcChromaRange: voodoo->bltSrcChromaRange = val; voodoo->bltSrcChromaMinB = val & 0x1f; voodoo->bltSrcChromaMinG = (val >> 5) & 0x3f; voodoo->bltSrcChromaMinR = (val >> 11) & 0x1f; voodoo->bltSrcChromaMaxB = (val >> 16) & 0x1f; voodoo->bltSrcChromaMaxG = (val >> 21) & 0x3f; voodoo->bltSrcChromaMaxR = (val >> 27) & 0x1f; break; case SST_bltDstChromaRange: voodoo->bltDstChromaRange = val; voodoo->bltDstChromaMinB = val & 0x1f; voodoo->bltDstChromaMinG = (val >> 5) & 0x3f; voodoo->bltDstChromaMinR = (val >> 11) & 0x1f; voodoo->bltDstChromaMaxB = (val >> 16) & 0x1f; voodoo->bltDstChromaMaxG = (val >> 21) & 0x3f; voodoo->bltDstChromaMaxR = (val >> 27) & 0x1f; break; case SST_bltClipX: voodoo->bltClipRight = val & 0xfff; voodoo->bltClipLeft = (val >> 16) & 0xfff; break; case SST_bltClipY: voodoo->bltClipHighY = val & 0xfff; voodoo->bltClipLowY = (val >> 16) & 0xfff; break; case SST_bltSrcXY: voodoo->bltSrcX = val & 0x7ff; voodoo->bltSrcY = (val >> 16) & 0x7ff; break; case SST_bltDstXY: // pclog("Write bltDstXY %08x\n", val); voodoo->bltDstX = val & 0x7ff; voodoo->bltDstY = (val >> 16) & 0x7ff; if (val & (1 << 31)) blit_start(voodoo); break; case SST_bltSize: // pclog("Write bltSize %08x\n", val); voodoo->bltSizeX = val & 0xfff; if (voodoo->bltSizeX & 0x800) voodoo->bltSizeX |= 0xfffff000; voodoo->bltSizeY = (val >> 16) & 0xfff; if (voodoo->bltSizeY & 0x800) voodoo->bltSizeY |= 0xfffff000; if (val & (1 << 31)) blit_start(voodoo); break; case SST_bltRop: voodoo->bltRop[0] = val & 0xf; voodoo->bltRop[1] = (val >> 4) & 0xf; voodoo->bltRop[2] = (val >> 8) & 0xf; voodoo->bltRop[3] = (val >> 12) & 0xf; break; case SST_bltColor: // pclog("Write bltColor %08x\n", val); voodoo->bltColorFg = val & 0xffff; voodoo->bltColorBg = (val >> 16) & 0xffff; break; case SST_bltCommand: voodoo->bltCommand = val; // pclog("Write bltCommand %08x\n", val); if (val & (1 << 31)) blit_start(voodoo); break; case SST_bltData: blit_data(voodoo, val); break; case SST_textureMode: if (chip & CHIP_TREX0) { voodoo->params.textureMode[0] = val; voodoo->params.tformat[0] = (val >> 8) & 0xf; } if (chip & CHIP_TREX1) { voodoo->params.textureMode[1] = val; voodoo->params.tformat[1] = (val >> 8) & 0xf; } break; case SST_tLOD: if (chip & CHIP_TREX0) { voodoo->params.tLOD[0] = val; voodoo_recalc_tex(voodoo, 0); } if (chip & CHIP_TREX1) { voodoo->params.tLOD[1] = val; voodoo_recalc_tex(voodoo, 1); } break; case SST_tDetail: if (chip & CHIP_TREX0) { voodoo->params.detail_max[0] = val & 0xff; voodoo->params.detail_bias[0] = (val >> 8) & 0x3f; voodoo->params.detail_scale[0] = (val >> 14) & 7; } if (chip & CHIP_TREX1) { voodoo->params.detail_max[1] = val & 0xff; voodoo->params.detail_bias[1] = (val >> 8) & 0x3f; voodoo->params.detail_scale[1] = (val >> 14) & 7; } break; case SST_texBaseAddr: if (chip & CHIP_TREX0) { voodoo->params.texBaseAddr[0] = (val & 0x7ffff) << 3; voodoo_recalc_tex(voodoo, 0); } if (chip & CHIP_TREX1) { voodoo->params.texBaseAddr[1] = (val & 0x7ffff) << 3; voodoo_recalc_tex(voodoo, 1); } break; case SST_texBaseAddr1: if (chip & CHIP_TREX0) { voodoo->params.texBaseAddr1[0] = (val & 0x7ffff) << 3; voodoo_recalc_tex(voodoo, 0); } if (chip & CHIP_TREX1) { voodoo->params.texBaseAddr1[1] = (val & 0x7ffff) << 3; voodoo_recalc_tex(voodoo, 1); } break; case SST_texBaseAddr2: if (chip & CHIP_TREX0) { voodoo->params.texBaseAddr2[0] = (val & 0x7ffff) << 3; voodoo_recalc_tex(voodoo, 0); } if (chip & CHIP_TREX1) { voodoo->params.texBaseAddr2[1] = (val & 0x7ffff) << 3; voodoo_recalc_tex(voodoo, 1); } break; case SST_texBaseAddr38: if (chip & CHIP_TREX0) { voodoo->params.texBaseAddr38[0] = (val & 0x7ffff) << 3; voodoo_recalc_tex(voodoo, 0); } if (chip & CHIP_TREX1) { voodoo->params.texBaseAddr38[1] = (val & 0x7ffff) << 3; voodoo_recalc_tex(voodoo, 1); } break; case SST_trexInit1: if (chip & CHIP_TREX0) voodoo->trexInit1[0] = val; if (chip & CHIP_TREX1) voodoo->trexInit1[1] = val; break; case SST_nccTable0_Y0: if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].y[0] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].y[0] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable0_Y1: if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].y[1] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].y[1] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable0_Y2: if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].y[2] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].y[2] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable0_Y3: if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].y[3] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].y[3] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable0_I0: if (!(val & (1 << 31))) { if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].i[0] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].i[0] = val; voodoo->ncc_dirty[1] = 1; } break; } case SST_nccTable0_I2: if (!(val & (1 << 31))) { if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].i[2] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].i[2] = val; voodoo->ncc_dirty[1] = 1; } break; } case SST_nccTable0_Q0: if (!(val & (1 << 31))) { if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].q[0] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].q[0] = val; voodoo->ncc_dirty[1] = 1; } break; } case SST_nccTable0_Q2: if (!(val & (1 << 31))) { if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].i[2] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].i[2] = val; voodoo->ncc_dirty[1] = 1; } break; } if (val & (1 << 31)) { int p = (val >> 23) & 0xfe; if (chip & CHIP_TREX0) { voodoo->palette[0][p].u = val | 0xff000000; voodoo->palette_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->palette[1][p].u = val | 0xff000000; voodoo->palette_dirty[1] = 1; } } break; case SST_nccTable0_I1: if (!(val & (1 << 31))) { if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].i[1] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].i[1] = val; voodoo->ncc_dirty[1] = 1; } break; } case SST_nccTable0_I3: if (!(val & (1 << 31))) { if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].i[3] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].i[3] = val; voodoo->ncc_dirty[1] = 1; } break; } case SST_nccTable0_Q1: if (!(val & (1 << 31))) { if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].q[1] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].q[1] = val; voodoo->ncc_dirty[1] = 1; } break; } case SST_nccTable0_Q3: if (!(val & (1 << 31))) { if (chip & CHIP_TREX0) { voodoo->nccTable[0][0].q[3] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][0].q[3] = val; voodoo->ncc_dirty[1] = 1; } break; } if (val & (1 << 31)) { int p = ((val >> 23) & 0xfe) | 0x01; if (chip & CHIP_TREX0) { voodoo->palette[0][p].u = val | 0xff000000; voodoo->palette_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->palette[1][p].u = val | 0xff000000; voodoo->palette_dirty[1] = 1; } } break; case SST_nccTable1_Y0: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].y[0] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].y[0] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable1_Y1: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].y[1] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].y[1] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable1_Y2: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].y[2] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].y[2] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable1_Y3: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].y[3] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].y[3] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable1_I0: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].i[0] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].i[0] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable1_I1: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].i[1] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].i[1] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable1_I2: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].i[2] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].i[2] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable1_I3: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].i[3] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].i[3] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable1_Q0: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].q[0] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].q[0] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable1_Q1: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].q[1] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].q[1] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable1_Q2: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].q[2] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].q[2] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_nccTable1_Q3: if (chip & CHIP_TREX0) { voodoo->nccTable[0][1].q[3] = val; voodoo->ncc_dirty[0] = 1; } if (chip & CHIP_TREX1) { voodoo->nccTable[1][1].q[3] = val; voodoo->ncc_dirty[1] = 1; } break; case SST_userIntrCMD: fatal("userIntrCMD write %08x from FIFO\n", val); break; } } static uint16_t voodoo_fb_readw(uint32_t addr, void *p) { voodoo_t *voodoo = (voodoo_t *)p; int x, y; uint32_t read_addr; uint16_t temp; x = (addr >> 1) & 0x3ff; y = (addr >> 11) & 0x3ff; read_addr = voodoo->fb_read_offset + (x << 1) + (y * voodoo->row_width); if (read_addr > voodoo->fb_mask) return 0xffff; temp = *(uint16_t *)(&voodoo->fb_mem[read_addr & voodoo->fb_mask]); // pclog("voodoo_fb_readw : %08X %08X %i %i %08X %08X %08x:%08x %i\n", addr, temp, x, y, read_addr, *(uint32_t *)(&voodoo->fb_mem[4]), cs, pc, fb_reads++); return temp; } static uint32_t voodoo_fb_readl(uint32_t addr, void *p) { voodoo_t *voodoo = (voodoo_t *)p; int x, y; uint32_t read_addr; uint32_t temp; x = addr & 0x7fe; y = (addr >> 11) & 0x3ff; read_addr = voodoo->fb_read_offset + x + (y * voodoo->row_width); if (read_addr > voodoo->fb_mask) return 0xffffffff; temp = *(uint32_t *)(&voodoo->fb_mem[read_addr & voodoo->fb_mask]); // pclog("voodoo_fb_readl : %08X %08x %08X x=%i y=%i %08X %08X %08x:%08x %i ro=%08x rw=%i\n", addr, read_addr, temp, x, y, read_addr, *(uint32_t *)(&voodoo->fb_mem[4]), cs, pc, fb_reads++, voodoo->fb_read_offset, voodoo->row_width); return temp; } static inline uint16_t do_dither(voodoo_params_t *params, rgba8_t col, int x, int y) { int r, g, b; if (dither) { if (dither2x2) { r = dither_rb2x2[col.r][y & 1][x & 1]; g = dither_g2x2[col.g][y & 1][x & 1]; b = dither_rb2x2[col.b][y & 1][x & 1]; } else { r = dither_rb[col.r][y & 3][x & 3]; g = dither_g[col.g][y & 3][x & 3]; b = dither_rb[col.b][y & 3][x & 3]; } } else { r = col.r >> 3; g = col.g >> 2; b = col.b >> 3; } return b | (g << 5) | (r << 11); } static void voodoo_fb_writew(uint32_t addr, uint16_t val, void *p) { voodoo_t *voodoo = (voodoo_t *)p; voodoo_params_t *params = &voodoo->params; int x, y; uint32_t write_addr, write_addr_aux; rgba8_t colour_data; uint16_t depth_data; uint8_t alpha_data; int write_mask; depth_data = voodoo->params.zaColor & 0xffff; alpha_data = voodoo->params.zaColor >> 24; // while (!RB_EMPTY) // thread_reset_event(voodoo->not_full_event); // pclog("voodoo_fb_writew : %08X %04X\n", addr, val); switch (voodoo->lfbMode & LFB_FORMAT_MASK) { case LFB_FORMAT_RGB565: colour_data = rgb565[val]; alpha_data = 0xff; write_mask = LFB_WRITE_COLOUR; break; case LFB_FORMAT_RGB555: colour_data = argb1555[val]; alpha_data = 0xff; write_mask = LFB_WRITE_COLOUR; break; case LFB_FORMAT_ARGB1555: colour_data = argb1555[val]; alpha_data = colour_data.a; write_mask = LFB_WRITE_COLOUR; break; case LFB_FORMAT_DEPTH: depth_data = val; write_mask = LFB_WRITE_DEPTH; break; default: fatal("voodoo_fb_writew : bad LFB format %08X\n", voodoo->lfbMode); } x = addr & 0x7fe; y = (addr >> 11) & 0x3ff; if (voodoo->fb_write_offset == voodoo->params.front_offset) voodoo->dirty_line[y] = 1; write_addr = voodoo->fb_write_offset + x + (y * voodoo->row_width); write_addr_aux = voodoo->params.aux_offset + x + (y * voodoo->row_width); // pclog("fb_writew %08x %i %i %i %08x\n", addr, x, y, voodoo->row_width, write_addr); if (voodoo->lfbMode & 0x100) { { rgba8_t write_data = colour_data; uint16_t new_depth = depth_data; if (params->fbzMode & FBZ_DEPTH_ENABLE) { uint16_t old_depth = *(uint16_t *)(&voodoo->fb_mem[write_addr_aux & voodoo->fb_mask]); DEPTH_TEST(new_depth); } if ((params->fbzMode & FBZ_CHROMAKEY) && write_data.r == params->chromaKey_r && write_data.g == params->chromaKey_g && write_data.b == params->chromaKey_b) goto skip_pixel; if (params->fogMode & FOG_ENABLE) { int32_t z = new_depth << 12; int32_t w_depth = new_depth; int32_t ia = alpha_data << 12; APPLY_FOG(write_data.r, write_data.g, write_data.b, z, ia, w_depth); } if (params->alphaMode & 1) ALPHA_TEST(alpha_data); if (params->alphaMode & (1 << 4)) { uint16_t dat = *(uint16_t *)(&voodoo->fb_mem[write_addr & voodoo->fb_mask]); int dest_r, dest_g, dest_b, dest_a; dest_r = (dat >> 8) & 0xf8; dest_g = (dat >> 3) & 0xfc; dest_b = (dat << 3) & 0xf8; dest_r |= (dest_r >> 5); dest_g |= (dest_g >> 6); dest_b |= (dest_b >> 5); dest_a = 0xff; ALPHA_BLEND(write_data.r, write_data.g, write_data.b, alpha_data); } if (params->fbzMode & FBZ_RGB_WMASK) *(uint16_t *)(&voodoo->fb_mem[write_addr & voodoo->fb_mask]) = do_dither(&voodoo->params, write_data, x >> 1, y); if (params->fbzMode & FBZ_DEPTH_WMASK) *(uint16_t *)(&voodoo->fb_mem[write_addr_aux & voodoo->fb_mask]) = new_depth; skip_pixel: x = x; } } else { if (write_mask & LFB_WRITE_COLOUR) *(uint16_t *)(&voodoo->fb_mem[write_addr & voodoo->fb_mask]) = do_dither(&voodoo->params, colour_data, x >> 1, y); if (write_mask & LFB_WRITE_DEPTH) *(uint16_t *)(&voodoo->fb_mem[write_addr_aux & voodoo->fb_mask]) = depth_data; } } static void voodoo_fb_writel(uint32_t addr, uint32_t val, void *p) { voodoo_t *voodoo = (voodoo_t *)p; voodoo_params_t *params = &voodoo->params; int x, y; uint32_t write_addr, write_addr_aux; rgba8_t colour_data[2]; uint16_t depth_data[2]; uint8_t alpha_data[2]; int write_mask, count = 1; depth_data[0] = depth_data[1] = voodoo->params.zaColor & 0xffff; alpha_data[0] = alpha_data[1] = voodoo->params.zaColor >> 24; // while (!RB_EMPTY) // thread_reset_event(voodoo->not_full_event); // pclog("voodoo_fb_writel : %08X %08X\n", addr, val); switch (voodoo->lfbMode & LFB_FORMAT_MASK) { case LFB_FORMAT_RGB565: colour_data[0] = rgb565[val & 0xffff]; colour_data[1] = rgb565[val >> 16]; write_mask = LFB_WRITE_COLOUR; count = 2; break; case LFB_FORMAT_RGB555: colour_data[0] = argb1555[val & 0xffff]; colour_data[1] = argb1555[val >> 16]; write_mask = LFB_WRITE_COLOUR; count = 2; break; case LFB_FORMAT_ARGB1555: colour_data[0] = argb1555[val & 0xffff]; alpha_data[0] = colour_data[0].a; colour_data[1] = argb1555[val >> 16]; alpha_data[1] = colour_data[1].a; write_mask = LFB_WRITE_COLOUR; count = 2; break; case LFB_FORMAT_ARGB8888: colour_data[0].b = val & 0xff; colour_data[0].g = (val >> 8) & 0xff; colour_data[0].r = (val >> 16) & 0xff; alpha_data[0] = (val >> 24) & 0xff; write_mask = LFB_WRITE_COLOUR; addr >>= 1; break; case LFB_FORMAT_DEPTH: depth_data[0] = val; depth_data[1] = val >> 16; write_mask = LFB_WRITE_DEPTH; count = 2; break; default: fatal("voodoo_fb_writel : bad LFB format %08X\n", voodoo->lfbMode); } x = addr & 0x7fe; y = (addr >> 11) & 0x3ff; if (voodoo->fb_write_offset == voodoo->params.front_offset) voodoo->dirty_line[y] = 1; write_addr = voodoo->fb_write_offset + x + (y * voodoo->row_width); write_addr_aux = voodoo->params.aux_offset + x + (y * voodoo->row_width); // pclog("fb_writel %08x x=%i y=%i rw=%i %08x wo=%08x\n", addr, x, y, voodoo->row_width, write_addr, voodoo->fb_write_offset); if (voodoo->lfbMode & 0x100) { int c; for (c = 0; c < count; c++) { rgba8_t write_data = colour_data[c]; uint16_t new_depth = depth_data[c]; if (params->fbzMode & FBZ_DEPTH_ENABLE) { uint16_t old_depth = *(uint16_t *)(&voodoo->fb_mem[write_addr_aux & voodoo->fb_mask]); DEPTH_TEST(new_depth); } if ((params->fbzMode & FBZ_CHROMAKEY) && write_data.r == params->chromaKey_r && write_data.g == params->chromaKey_g && write_data.b == params->chromaKey_b) goto skip_pixel; if (params->fogMode & FOG_ENABLE) { int32_t z = new_depth << 12; int32_t w_depth = new_depth; int32_t ia = alpha_data[c] << 12; APPLY_FOG(write_data.r, write_data.g, write_data.b, z, ia, w_depth); } if (params->alphaMode & 1) ALPHA_TEST(alpha_data[c]); if (params->alphaMode & (1 << 4)) { uint16_t dat = *(uint16_t *)(&voodoo->fb_mem[write_addr & voodoo->fb_mask]); int dest_r, dest_g, dest_b, dest_a; dest_r = (dat >> 8) & 0xf8; dest_g = (dat >> 3) & 0xfc; dest_b = (dat << 3) & 0xf8; dest_r |= (dest_r >> 5); dest_g |= (dest_g >> 6); dest_b |= (dest_b >> 5); dest_a = 0xff; ALPHA_BLEND(write_data.r, write_data.g, write_data.b, alpha_data[c]); } if (params->fbzMode & FBZ_RGB_WMASK) *(uint16_t *)(&voodoo->fb_mem[write_addr & voodoo->fb_mask]) = do_dither(&voodoo->params, write_data, (x >> 1) + c, y); if (params->fbzMode & FBZ_DEPTH_WMASK) *(uint16_t *)(&voodoo->fb_mem[write_addr_aux & voodoo->fb_mask]) = new_depth; skip_pixel: write_addr += 2; write_addr_aux += 2; } } else { int c; for (c = 0; c < count; c++) { if (write_mask & LFB_WRITE_COLOUR) *(uint16_t *)(&voodoo->fb_mem[write_addr & voodoo->fb_mask]) = do_dither(&voodoo->params, colour_data[c], (x >> 1) + c, y); if (write_mask & LFB_WRITE_DEPTH) *(uint16_t *)(&voodoo->fb_mem[write_addr_aux & voodoo->fb_mask]) = depth_data[c]; write_addr += 2; write_addr_aux += 2; } } } static void voodoo_tex_writel(uint32_t addr, uint32_t val, void *p) { int lod, s, t; voodoo_t *voodoo = (voodoo_t *)p; int tmu; if (addr & 0x400000) return; /*TREX != 0*/ tmu = (addr & 0x200000) ? 1 : 0; if (tmu && !voodoo->dual_tmus) return; // pclog("voodoo_tex_writel : %08X %08X %i\n", addr, val, voodoo->params.tformat); lod = (addr >> 17) & 0xf; t = (addr >> 9) & 0xff; if (voodoo->params.tformat[tmu] & 8) s = (addr >> 1) & 0xfe; else { if (voodoo->params.textureMode[tmu] & (1 << 31)) s = addr & 0xfc; else s = (addr >> 1) & 0xfc; } if (lod > LOD_MAX) return; // if (addr >= 0x200000) // return; if (voodoo->params.tformat[tmu] & 8) addr = voodoo->params.tex_base[tmu][lod] + s*2 + (t << voodoo->params.tex_shift[tmu][lod])*2; else addr = voodoo->params.tex_base[tmu][lod] + s + (t << voodoo->params.tex_shift[tmu][lod]); if (voodoo->texture_present[tmu][(addr & voodoo->texture_mask) >> TEX_DIRTY_SHIFT]) { // pclog("texture_present at %08x %i\n", addr, (addr & voodoo->texture_mask) >> TEX_DIRTY_SHIFT); flush_texture_cache(voodoo, addr & voodoo->texture_mask, tmu); } *(uint32_t *)(&voodoo->tex_mem[tmu][addr & voodoo->texture_mask]) = val; } #define WAKE_DELAY (TIMER_USEC * 100) static inline void wake_fifo_thread(voodoo_t *voodoo) { if (!voodoo->wake_timer) { /*Don't wake FIFO thread immediately - if we do that it will probably process one word and go back to sleep, requiring it to be woken on almost every write. Instead, wait a short while so that the CPU emulation writes more data so we have more batched-up work.*/ timer_process(); voodoo->wake_timer = WAKE_DELAY; timer_update_outstanding(); } } static inline void wake_fifo_thread_now(voodoo_t *voodoo) { thread_set_event(voodoo->wake_fifo_thread); /*Wake up FIFO thread if moving from idle*/ } static void voodoo_wake_timer(void *p) { voodoo_t *voodoo = (voodoo_t *)p; voodoo->wake_timer = 0; thread_set_event(voodoo->wake_fifo_thread); /*Wake up FIFO thread if moving from idle*/ } static inline void queue_command(voodoo_t *voodoo, uint32_t addr_type, uint32_t val) { fifo_entry_t *fifo = &voodoo->fifo[voodoo->fifo_write_idx & FIFO_MASK]; int c; if (FIFO_FULL) { thread_reset_event(voodoo->fifo_not_full_event); if (FIFO_FULL) { thread_wait_event(voodoo->fifo_not_full_event, -1); /*Wait for room in ringbuffer*/ } } fifo->val = val; fifo->addr_type = addr_type; voodoo->fifo_write_idx++; if (FIFO_ENTRIES > 0xe000) wake_fifo_thread(voodoo); } static uint16_t voodoo_readw(uint32_t addr, void *p) { voodoo_t *voodoo = (voodoo_t *)p; addr &= 0xffffff; cycles -= voodoo->read_time; if ((addr & 0xc00000) == 0x400000) /*Framebuffer*/ { voodoo->flush = 1; while (!FIFO_EMPTY) { wake_fifo_thread_now(voodoo); thread_wait_event(voodoo->fifo_not_full_event, 1); } wait_for_render_thread_idle(voodoo); voodoo->flush = 0; return voodoo_fb_readw(addr, voodoo); } return 0xffff; } static void voodoo_flush(voodoo_t *voodoo) { voodoo->flush = 1; while (!FIFO_EMPTY) { wake_fifo_thread_now(voodoo); thread_wait_event(voodoo->fifo_not_full_event, 1); } wait_for_render_thread_idle(voodoo); voodoo->flush = 0; } static uint32_t voodoo_readl(uint32_t addr, void *p) { voodoo_t *voodoo = (voodoo_t *)p; uint32_t temp; int fifo_size; voodoo->rd_count++; addr &= 0xffffff; cycles -= voodoo->read_time; if (addr & 0x800000) /*Texture*/ { } else if (addr & 0x400000) /*Framebuffer*/ { voodoo->flush = 1; while (!FIFO_EMPTY) { wake_fifo_thread_now(voodoo); thread_wait_event(voodoo->fifo_not_full_event, 1); } wait_for_render_thread_idle(voodoo); voodoo->flush = 0; temp = voodoo_fb_readl(addr, voodoo); } else switch (addr & 0x3fc) { case SST_status: fifo_size = 0xffff - FIFO_ENTRIES; temp = fifo_size << 12; if (fifo_size < 0x40) temp |= fifo_size; else temp |= 0x3f; temp |= (voodoo->swap_count << 28); if (voodoo->cmd_written - voodoo->cmd_read) temp |= 0x380; /*Busy*/ if (voodoo->cmdfifo_depth_rd != voodoo->cmdfifo_depth_wr) temp |= 0x380; /*Busy*/ if (!voodoo->v_retrace) temp |= 0x40; if (!voodoo->voodoo_busy) 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 & ~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; case SST_hvRetrace: timer_clock(); temp = voodoo->line & 0x1fff; temp |= ((((voodoo->line_time - voodoo->timer_count) * voodoo->h_total) / voodoo->timer_count) << 16) & 0x7ff0000; break; case SST_fbiInit5: temp = voodoo->fbiInit5 & ~0x41ff; 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: fatal("voodoo_readl : bad addr %08X\n", addr); temp = 0xffffffff; } return temp; } static void voodoo_writew(uint32_t addr, uint16_t val, void *p) { voodoo_t *voodoo = (voodoo_t *)p; 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 & 0xc00000) == 0x400000) /*Framebuffer*/ queue_command(voodoo, addr | FIFO_WRITEW_FB, val); } static void voodoo_pixelclock_update(voodoo_t *voodoo) { int m = (voodoo->dac_pll_regs[0] & 0x7f) + 2; int n1 = ((voodoo->dac_pll_regs[0] >> 8) & 0x1f) + 2; int n2 = ((voodoo->dac_pll_regs[0] >> 13) & 0x07); float t = (14318184.0 * ((float)m / (float)n1)) / (float)(1 << n2); double clock_const; int line_length; if ((voodoo->dac_data[6] & 0xf0) == 0x20 || (voodoo->dac_data[6] & 0xf0) == 0x60 || (voodoo->dac_data[6] & 0xf0) == 0x70) t /= 2.0f; line_length = (voodoo->hSync & 0xff) + ((voodoo->hSync >> 16) & 0x3ff); // pclog("Pixel clock %f MHz hsync %08x line_length %d\n", t, voodoo->hSync, line_length); voodoo->pixel_clock = t; clock_const = cpuclock / t; voodoo->line_time = (int)((double)line_length * clock_const * (double)(1 << TIMER_SHIFT)); } static void voodoo_writel(uint32_t addr, uint32_t val, void *p) { voodoo_t *voodoo = (voodoo_t *)p; union { uint32_t i; float f; } tempif; tempif.i = val; 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++; queue_command(voodoo, addr | FIFO_WRITEL_TEX, val); } else if (addr & 0x400000) /*Framebuffer*/ { queue_command(voodoo, addr | FIFO_WRITEL_FB, val); } else if ((addr & 0x200000) && (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE)) { // pclog("Write CMDFIFO %08x(%08x) %08x %08x\n", addr, voodoo->cmdfifo_base + (addr & 0x3fffc), val, (voodoo->cmdfifo_base + (addr & 0x3fffc)) & voodoo->fb_mask); *(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) 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++; voodoo->swap_count++; if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE) return; queue_command(voodoo, addr | FIFO_WRITEL_REG, val); if (!voodoo->voodoo_busy) wake_fifo_thread(voodoo); break; case SST_triangleCMD: if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE) return; voodoo->cmd_written++; queue_command(voodoo, addr | FIFO_WRITEL_REG, val); if (!voodoo->voodoo_busy) wake_fifo_thread(voodoo); break; case SST_ftriangleCMD: if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE) return; voodoo->cmd_written++; queue_command(voodoo, addr | FIFO_WRITEL_REG, val); if (!voodoo->voodoo_busy) wake_fifo_thread(voodoo); break; case SST_fastfillCMD: if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE) return; voodoo->cmd_written++; queue_command(voodoo, addr | FIFO_WRITEL_REG, val); if (!voodoo->voodoo_busy) wake_fifo_thread(voodoo); break; case SST_nopCMD: if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE) return; voodoo->cmd_written++; queue_command(voodoo, addr | FIFO_WRITEL_REG, val); if (!voodoo->voodoo_busy) wake_fifo_thread(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); // pclog("fbiInit4 write %08x - read_time=%i\n", val, voodoo->read_time); } 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; break; case SST_fbiInit0: if (voodoo->initEnable & 0x01) { voodoo->fbiInit0 = val; 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; } } break; case SST_fbiInit1: if (voodoo->initEnable & 0x01) { voodoo->fbiInit1 = val; 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); // pclog("fbiInit1 write %08x - write_time=%i burst_time=%i\n", val, voodoo->write_time, voodoo->burst_time); } 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) { // 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 { 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); // pclog("Write PLL reg %x %04x\n", voodoo->dac_data[4] & 0xf, voodoo->dac_pll_regs[voodoo->dac_data[4] & 0xf]); 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); pclog("Voodoo Filter: %06x\n", val); } break; case SST_fbiInit5: if (voodoo->initEnable & 0x01) voodoo->fbiInit5 = val; break; case SST_fbiInit6: if (voodoo->initEnable & 0x01) voodoo->fbiInit6 = val; break; case SST_fbiInit7: if (voodoo->initEnable & 0x01) voodoo->fbiInit7 = val; break; case SST_cmdFifoBaseAddr: voodoo->cmdfifo_base = (val & 0x3ff) << 12; voodoo->cmdfifo_end = ((val >> 16) & 0x3ff) << 12; // pclog("CMDFIFO base=%08x end=%08x\n", voodoo->cmdfifo_base, voodoo->cmdfifo_end); 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) { fatal("Unknown register write in CMDFIFO mode %08x %08x\n", addr, val); } else { queue_command(voodoo, addr | FIFO_WRITEL_REG, val); } break; } } static uint32_t cmdfifo_get(voodoo_t *voodoo) { uint32_t val; while (voodoo->cmdfifo_depth_rd == voodoo->cmdfifo_depth_wr) { thread_wait_event(voodoo->wake_fifo_thread, -1); thread_reset_event(voodoo->wake_fifo_thread); } val = *(uint32_t *)&voodoo->fb_mem[voodoo->cmdfifo_rp & voodoo->fb_mask]; voodoo->cmdfifo_depth_rd++; voodoo->cmdfifo_rp += 4; // pclog(" CMDFIFO get %08x\n", val); return val; } static inline float cmdfifo_get_f(voodoo_t *voodoo) { union { uint32_t i; float f; } tempif; tempif.i = cmdfifo_get(voodoo); return tempif.f; } enum { CMDFIFO3_PC_MASK_RGB = (1 << 10), CMDFIFO3_PC_MASK_ALPHA = (1 << 11), CMDFIFO3_PC_MASK_Z = (1 << 12), CMDFIFO3_PC_MASK_Wb = (1 << 13), CMDFIFO3_PC_MASK_W0 = (1 << 14), CMDFIFO3_PC_MASK_S0_T0 = (1 << 15), CMDFIFO3_PC_MASK_W1 = (1 << 16), CMDFIFO3_PC_MASK_S1_T1 = (1 << 17), CMDFIFO3_PC = (1 << 28) }; static void fifo_thread(void *param) { voodoo_t *voodoo = (voodoo_t *)param; while (1) { thread_set_event(voodoo->fifo_not_full_event); thread_wait_event(voodoo->wake_fifo_thread, -1); thread_reset_event(voodoo->wake_fifo_thread); voodoo->voodoo_busy = 1; while (!FIFO_EMPTY) { uint64_t start_time = timer_read(); uint64_t end_time; fifo_entry_t *fifo = &voodoo->fifo[voodoo->fifo_read_idx & FIFO_MASK]; switch (fifo->addr_type & FIFO_TYPE) { case FIFO_WRITEL_REG: voodoo_reg_writel(fifo->addr_type & FIFO_ADDR, fifo->val, voodoo); break; case FIFO_WRITEW_FB: wait_for_render_thread_idle(voodoo); voodoo_fb_writew(fifo->addr_type & FIFO_ADDR, fifo->val, voodoo); break; case FIFO_WRITEL_FB: wait_for_render_thread_idle(voodoo); voodoo_fb_writel(fifo->addr_type & FIFO_ADDR, fifo->val, voodoo); break; case FIFO_WRITEL_TEX: if (!(fifo->addr_type & 0x400000)) voodoo_tex_writel(fifo->addr_type & FIFO_ADDR, fifo->val, voodoo); break; } voodoo->fifo_read_idx++; fifo->addr_type = FIFO_INVALID; if (FIFO_ENTRIES > 0xe000) thread_set_event(voodoo->fifo_not_full_event); end_time = timer_read(); voodoo_time += end_time - start_time; } while (voodoo->cmdfifo_depth_rd != voodoo->cmdfifo_depth_wr) { uint64_t start_time = timer_read(); uint64_t end_time; uint32_t header = cmdfifo_get(voodoo); uint32_t addr; uint32_t mask; int smode; int num; int num_verticies; int v_num; // pclog(" CMDFIFO header %08x at %08x\n", header, voodoo->cmdfifo_rp); switch (header & 7) { case 0: // pclog("CMDFIFO0\n"); switch ((header >> 3) & 7) { case 0: /*NOP*/ break; case 3: /*JMP local frame buffer*/ voodoo->cmdfifo_rp = (header >> 4) & 0xfffffc; // pclog("JMP to %08x %04x\n", voodoo->cmdfifo_rp, header); break; default: fatal("Bad CMDFIFO0 %08x\n", header); } break; case 1: num = header >> 16; addr = (header & 0x7ff8) >> 1; // pclog("CMDFIFO1 addr=%08x\n",addr); while (num--) { uint32_t val = cmdfifo_get(voodoo); if ((addr & 0x3ff) == SST_triangleCMD || (addr & 0x3ff) == SST_ftriangleCMD || (addr & 0x3ff) == SST_fastfillCMD || (addr & 0x3ff) == SST_nopCMD) voodoo->cmd_written++; voodoo_reg_writel(addr, val, voodoo); if (header & (1 << 15)) addr += 4; } break; case 3: num = (header >> 29) & 7; mask = header;//(header >> 10) & 0xff; smode = (header >> 22) & 0xf; voodoo_reg_writel(SST_sSetupMode, ((header >> 10) & 0xff) | (smode << 16), voodoo); num_verticies = (header >> 6) & 0xf; v_num = 0; if (((header >> 3) & 7) == 2) v_num = 1; // pclog("CMDFIFO3: num=%i verts=%i mask=%02x\n", num, num_verticies, (header >> 10) & 0xff); // pclog("CMDFIFO3 %02x %i\n", (header >> 10), (header >> 3) & 7); while (num_verticies--) { voodoo->verts[3].sVx = cmdfifo_get_f(voodoo); voodoo->verts[3].sVy = cmdfifo_get_f(voodoo); if (mask & CMDFIFO3_PC_MASK_RGB) { if (header & CMDFIFO3_PC) { uint32_t val = cmdfifo_get(voodoo); voodoo->verts[3].sBlue = (float)(val & 0xff); voodoo->verts[3].sGreen = (float)((val >> 8) & 0xff); voodoo->verts[3].sRed = (float)((val >> 16) & 0xff); voodoo->verts[3].sAlpha = (float)((val >> 24) & 0xff); } else { voodoo->verts[3].sRed = cmdfifo_get_f(voodoo); voodoo->verts[3].sGreen = cmdfifo_get_f(voodoo); voodoo->verts[3].sBlue = cmdfifo_get_f(voodoo); } } if ((mask & CMDFIFO3_PC_MASK_ALPHA) && !(header & CMDFIFO3_PC)) voodoo->verts[3].sAlpha = cmdfifo_get_f(voodoo); if (mask & CMDFIFO3_PC_MASK_Z) voodoo->verts[3].sVz = cmdfifo_get_f(voodoo); if (mask & CMDFIFO3_PC_MASK_Wb) voodoo->verts[3].sWb = cmdfifo_get_f(voodoo); if (mask & CMDFIFO3_PC_MASK_W0) voodoo->verts[3].sW0 = cmdfifo_get_f(voodoo); if (mask & CMDFIFO3_PC_MASK_S0_T0) { voodoo->verts[3].sS0 = cmdfifo_get_f(voodoo); voodoo->verts[3].sT0 = cmdfifo_get_f(voodoo); } if (mask & CMDFIFO3_PC_MASK_W1) voodoo->verts[3].sW1 = cmdfifo_get_f(voodoo); if (mask & CMDFIFO3_PC_MASK_S1_T1) { voodoo->verts[3].sS1 = cmdfifo_get_f(voodoo); voodoo->verts[3].sT1 = cmdfifo_get_f(voodoo); } if (v_num) voodoo_reg_writel(SST_sDrawTriCMD, 0, voodoo); else voodoo_reg_writel(SST_sBeginTriCMD, 0, voodoo); v_num++; if (v_num == 3 && ((header >> 3) & 7) == 0) v_num = 0; } break; case 4: num = (header >> 29) & 7; mask = (header >> 15) & 0x3fff; addr = (header & 0x7ff8) >> 1; // pclog("CMDFIFO4 addr=%08x\n",addr); while (mask) { if (mask & 1) { uint32_t val = cmdfifo_get(voodoo); if ((addr & 0x3ff) == SST_triangleCMD || (addr & 0x3ff) == SST_ftriangleCMD || (addr & 0x3ff) == SST_fastfillCMD || (addr & 0x3ff) == SST_nopCMD) voodoo->cmd_written++; voodoo_reg_writel(addr, val, voodoo); } addr += 4; mask >>= 1; } while (num--) cmdfifo_get(voodoo); break; case 5: if (header & 0x3fc0000) fatal("CMDFIFO packet 5 has byte disables set %08x\n", header); num = (header >> 3) & 0x7ffff; addr = cmdfifo_get(voodoo) & 0xffffff; // pclog("CMDFIFO5 addr=%08x num=%i\n", addr, num); switch (header >> 30) { case 2: /*Framebuffer*/ while (num--) { uint32_t val = cmdfifo_get(voodoo); voodoo_fb_writel(addr, val, voodoo); addr += 4; } break; case 3: /*Texture*/ while (num--) { uint32_t val = cmdfifo_get(voodoo); voodoo_tex_writel(addr, val, voodoo); addr += 4; } break; default: fatal("CMDFIFO packet 5 bad space %08x %08x\n", header, voodoo->cmdfifo_rp); } break; default: pclog("Bad CMDFIFO packet %08x %08x\n", header, voodoo->cmdfifo_rp); } end_time = timer_read(); voodoo_time += end_time - start_time; } voodoo->voodoo_busy = 0; } } static void voodoo_recalcmapping(voodoo_t *voodoo) { if (voodoo->pci_enable && voodoo->memBaseAddr) { pclog("voodoo_recalcmapping : memBaseAddr %08X\n", voodoo->memBaseAddr); mem_mapping_set_addr(&voodoo->mapping, voodoo->memBaseAddr, 0x01000000); } else { pclog("voodoo_recalcmapping : disabled\n"); mem_mapping_disable(&voodoo->mapping); } } uint8_t voodoo_pci_read(int func, int addr, void *p) { voodoo_t *voodoo = (voodoo_t *)p; if (func) return 0; // pclog("Voodoo PCI read %08X PC=%08x\n", addr, cpu_state.pc); 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; } return 0; } void voodoo_pci_write(int func, int addr, uint8_t val, void *p) { voodoo_t *voodoo = (voodoo_t *)p; if (func) return; // pclog("Voodoo PCI write %04X %02X PC=%08x\n", addr, val, cpu_state.pc); switch (addr) { case 0x04: voodoo->pci_enable = val & 2; voodoo_recalcmapping(voodoo); break; case 0x13: voodoo->memBaseAddr = val << 24; voodoo_recalcmapping(voodoo); 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); break; case 0x43: voodoo->initEnable = (voodoo->initEnable & ~0xff000000) | (val << 24); break; } } static void voodoo_calc_clutData(voodoo_t *voodoo) { int c; for (c = 0; c < 256; c++) { voodoo->clutData256[c].r = (voodoo->clutData[c >> 3].r*(8-(c & 7)) + voodoo->clutData[(c >> 3)+1].r*(c & 7)) >> 3; voodoo->clutData256[c].g = (voodoo->clutData[c >> 3].g*(8-(c & 7)) + voodoo->clutData[(c >> 3)+1].g*(c & 7)) >> 3; voodoo->clutData256[c].b = (voodoo->clutData[c >> 3].b*(8-(c & 7)) + voodoo->clutData[(c >> 3)+1].b*(c & 7)) >> 3; } for (c = 0; c < 65536; c++) { int r = (c >> 8) & 0xf8; int g = (c >> 3) & 0xfc; int b = (c << 3) & 0xf8; // r |= (r >> 5); // g |= (g >> 6); // b |= (b >> 5); voodoo->video_16to32[c] = (voodoo->clutData256[r].r << 16) | (voodoo->clutData256[g].g << 8) | voodoo->clutData256[b].b; } } #define FILTDIV 256 static int FILTCAP, FILTCAPG, FILTCAPB = 0; /* color filter threshold values */ static void voodoo_generate_filter_v1(voodoo_t *voodoo) { int g, h; float difference, diffg, diffb; float thiscol, thiscolg, thiscolb, lined; float fcr, fcg, fcb; fcr = FILTCAP * 5; fcg = FILTCAPG * 6; fcb = FILTCAPB * 5; for (g=0;g FILTCAP) difference = FILTCAP; if (difference < -FILTCAP) difference = -FILTCAP; if (diffg > FILTCAPG) diffg = FILTCAPG; if (diffg < -FILTCAPG) diffg = -FILTCAPG; if (diffb > FILTCAPB) diffb = FILTCAPB; if (diffb < -FILTCAPB) diffb = -FILTCAPB; // hack - to make it not bleed onto black //if (g == 0){ //difference = diffg = diffb = 0; //} if ((difference < fcr) || (-difference > -fcr)) thiscol = g + (difference / 2); if ((diffg < fcg) || (-diffg > -fcg)) thiscolg = g + (diffg / 2); /* need these divides so we can actually undither! */ if ((diffb < fcb) || (-diffb > -fcb)) thiscolb = g + (diffb / 2); if (thiscol < 0) thiscol = 0; if (thiscol > FILTDIV-1) thiscol = FILTDIV-1; if (thiscolg < 0) thiscolg = 0; if (thiscolg > FILTDIV-1) thiscolg = FILTDIV-1; if (thiscolb < 0) thiscolb = 0; if (thiscolb > FILTDIV-1) thiscolb = FILTDIV-1; voodoo->thefilter[g][h] = thiscol; voodoo->thefilterg[g][h] = thiscolg; voodoo->thefilterb[g][h] = thiscolb; } lined = g + 4; if (lined > 255) lined = 255; voodoo->purpleline[g][0] = lined; voodoo->purpleline[g][2] = lined; lined = g + 0; if (lined > 255) lined = 255; voodoo->purpleline[g][1] = lined; } } static void voodoo_generate_filter_v2(voodoo_t *voodoo) { int g, h; float difference; float thiscol, thiscolg, thiscolb, lined; float clr, clg, clb = 0; float fcr, fcg, fcb = 0; // pre-clamping fcr = FILTCAP; fcg = FILTCAPG; fcb = FILTCAPB; if (fcr > 32) fcr = 32; if (fcg > 32) fcg = 32; if (fcb > 32) fcb = 32; for (g=0;g<256;g++) // pixel 1 - our target pixel we want to bleed into { for (h=0;h<256;h++) // pixel 2 - our main pixel { float avg; float avgdiff; difference = (float)(g - h); avg = (float)((g + g + g + g + h) / 5); avgdiff = avg - (float)((g + h + h + h + h) / 5); if (avgdiff < 0) avgdiff *= -1; if (difference < 0) difference *= -1; thiscol = thiscolg = thiscolb = g; // try lighten if (h > g) { clr = clg = clb = avgdiff; if (clr>fcr) clr=fcr; if (clg>fcg) clg=fcg; if (clb>fcb) clb=fcb; thiscol = g + clr; thiscolg = g + clg; thiscolb = g + clb; if (thiscol>g+FILTCAP) thiscol=g+FILTCAP; if (thiscolg>g+FILTCAPG) thiscolg=g+FILTCAPG; if (thiscolb>g+FILTCAPB) thiscolb=g+FILTCAPB; if (thiscol>g+avgdiff) thiscol=g+avgdiff; if (thiscolg>g+avgdiff) thiscolg=g+avgdiff; if (thiscolb>g+avgdiff) thiscolb=g+avgdiff; } if (difference > FILTCAP) thiscol = g; if (difference > FILTCAPG) thiscolg = g; if (difference > FILTCAPB) thiscolb = g; // clamp if (thiscol < 0) thiscol = 0; if (thiscolg < 0) thiscolg = 0; if (thiscolb < 0) thiscolb = 0; if (thiscol > 255) thiscol = 255; if (thiscolg > 255) thiscolg = 255; if (thiscolb > 255) thiscolb = 255; // add to the table voodoo->thefilter[g][h] = (thiscol); voodoo->thefilterg[g][h] = (thiscolg); voodoo->thefilterb[g][h] = (thiscolb); // debug the ones that don't give us much of a difference //if (difference < FILTCAP) //pclog("Voodoofilter: %ix%i - %f difference, %f average difference, R=%f, G=%f, B=%f\n", g, h, difference, avgdiff, thiscol, thiscolg, thiscolb); } lined = g + 3; if (lined > 255) lined = 255; voodoo->purpleline[g][0] = lined; voodoo->purpleline[g][1] = 0; voodoo->purpleline[g][2] = lined; } } static void voodoo_threshold_check(voodoo_t *voodoo) { int r, g, b; if (!voodoo->scrfilterEnabled) return; /* considered disabled; don't check and generate */ /* Check for changes, to generate anew table */ if (voodoo->scrfilterThreshold != voodoo->scrfilterThresholdOld) { r = (voodoo->scrfilterThreshold >> 16) & 0xFF; g = (voodoo->scrfilterThreshold >> 8 ) & 0xFF; b = voodoo->scrfilterThreshold & 0xFF; FILTCAP = r; FILTCAPG = g; FILTCAPB = b; pclog("Voodoo Filter Threshold Check: %06x - RED %i GREEN %i BLUE %i\n", voodoo->scrfilterThreshold, r, g, b); voodoo->scrfilterThresholdOld = voodoo->scrfilterThreshold; if (voodoo->type == VOODOO_2) voodoo_generate_filter_v2(voodoo); else voodoo_generate_filter_v1(voodoo); } } static void voodoo_filterline_v1(voodoo_t *voodoo, uint8_t *fil, int column, uint16_t *src, int line) { int x; // Scratchpad for avoiding feedback streaks uint8_t fil3[(voodoo->h_disp) * 3]; /* 16 to 32-bit */ for (x=0; x> 5) & 63) << 2); fil[x*3+2] = (((src[x] >> 11) & 31) << 3); // Copy to our scratchpads fil3[x*3+0] = fil[x*3+0]; fil3[x*3+1] = fil[x*3+1]; fil3[x*3+2] = fil[x*3+2]; } /* lines */ if (line & 1) { for (x=0; xpurpleline[fil[x*3]][0]; fil[x*3+1] = voodoo->purpleline[fil[x*3+1]][1]; fil[x*3+2] = voodoo->purpleline[fil[x*3+2]][2]; } } /* filtering time */ for (x=1; xthefilterb[fil[x*3]][fil[ (x-1) *3]]; fil3[(x)*3+1] = voodoo->thefilterg[fil[x*3+1]][fil[ (x-1) *3+1]]; fil3[(x)*3+2] = voodoo->thefilter[fil[x*3+2]][fil[ (x-1) *3+2]]; } for (x=1; xthefilterb[fil3[x*3]][fil3[ (x-1) *3]]; fil[(x)*3+1] = voodoo->thefilterg[fil3[x*3+1]][fil3[ (x-1) *3+1]]; fil[(x)*3+2] = voodoo->thefilter[fil3[x*3+2]][fil3[ (x-1) *3+2]]; } for (x=1; xthefilterb[fil[x*3]][fil[ (x-1) *3]]; fil3[(x)*3+1] = voodoo->thefilterg[fil[x*3+1]][fil[ (x-1) *3+1]]; fil3[(x)*3+2] = voodoo->thefilter[fil[x*3+2]][fil[ (x-1) *3+2]]; } for (x=0; xthefilterb[fil3[x*3]][fil3[ (x+1) *3]]; fil[(x)*3+1] = voodoo->thefilterg[fil3[x*3+1]][fil3[ (x+1) *3+1]]; fil[(x)*3+2] = voodoo->thefilter[fil3[x*3+2]][fil3[ (x+1) *3+2]]; } } static void voodoo_filterline_v2(voodoo_t *voodoo, uint8_t *fil, int column, uint16_t *src, int line) { int x; // Scratchpad for blending filter uint8_t fil3[(voodoo->h_disp) * 3]; /* 16 to 32-bit */ for (x=0; x> 5) & 63) << 2); fil3[x*3+2] = fil[x*3+2] = (((src[x] >> 11) & 31) << 3); } /* filtering time */ for (x=1; xthefilterb [((src[x+3] & 31) << 3)] [((src[x] & 31) << 3)]; fil3[(x+3)*3+1] = voodoo->thefilterg [(((src[x+3] >> 5) & 63) << 2)] [(((src[x] >> 5) & 63) << 2)]; fil3[(x+3)*3+2] = voodoo->thefilter [(((src[x+3] >> 11) & 31) << 3)] [(((src[x] >> 11) & 31) << 3)]; fil[(x+2)*3] = voodoo->thefilterb [fil3[(x+2)*3]][((src[x] & 31) << 3)]; fil[(x+2)*3+1] = voodoo->thefilterg [fil3[(x+2)*3+1]][(((src[x] >> 5) & 63) << 2)]; fil[(x+2)*3+2] = voodoo->thefilter [fil3[(x+2)*3+2]][(((src[x] >> 11) & 31) << 3)]; fil3[(x+1)*3] = voodoo->thefilterb [fil[(x+1)*3]][((src[x] & 31) << 3)]; fil3[(x+1)*3+1] = voodoo->thefilterg [fil[(x+1)*3+1]][(((src[x] >> 5) & 63) << 2)]; fil3[(x+1)*3+2] = voodoo->thefilter [fil[(x+1)*3+2]][(((src[x] >> 11) & 31) << 3)]; fil[(x-1)*3] = voodoo->thefilterb [fil3[(x-1)*3]][((src[x] & 31) << 3)]; fil[(x-1)*3+1] = voodoo->thefilterg [fil3[(x-1)*3+1]][(((src[x] >> 5) & 63) << 2)]; fil[(x-1)*3+2] = voodoo->thefilter [fil3[(x-1)*3+2]][(((src[x] >> 11) & 31) << 3)]; } // unroll for edge cases fil3[(column-3)*3] = voodoo->thefilterb [((src[column-3] & 31) << 3)] [((src[column] & 31) << 3)]; fil3[(column-3)*3+1] = voodoo->thefilterg [(((src[column-3] >> 5) & 63) << 2)] [(((src[column] >> 5) & 63) << 2)]; fil3[(column-3)*3+2] = voodoo->thefilter [(((src[column-3] >> 11) & 31) << 3)] [(((src[column] >> 11) & 31) << 3)]; fil3[(column-2)*3] = voodoo->thefilterb [((src[column-2] & 31) << 3)] [((src[column] & 31) << 3)]; fil3[(column-2)*3+1] = voodoo->thefilterg [(((src[column-2] >> 5) & 63) << 2)] [(((src[column] >> 5) & 63) << 2)]; fil3[(column-2)*3+2] = voodoo->thefilter [(((src[column-2] >> 11) & 31) << 3)] [(((src[column] >> 11) & 31) << 3)]; fil3[(column-1)*3] = voodoo->thefilterb [((src[column-1] & 31) << 3)] [((src[column] & 31) << 3)]; fil3[(column-1)*3+1] = voodoo->thefilterg [(((src[column-1] >> 5) & 63) << 2)] [(((src[column] >> 5) & 63) << 2)]; fil3[(column-1)*3+2] = voodoo->thefilter [(((src[column-1] >> 11) & 31) << 3)] [(((src[column] >> 11) & 31) << 3)]; fil[(column-2)*3] = voodoo->thefilterb [fil3[(column-2)*3]][((src[column] & 31) << 3)]; fil[(column-2)*3+1] = voodoo->thefilterg [fil3[(column-2)*3+1]][(((src[column] >> 5) & 63) << 2)]; fil[(column-2)*3+2] = voodoo->thefilter [fil3[(column-2)*3+2]][(((src[column] >> 11) & 31) << 3)]; fil[(column-1)*3] = voodoo->thefilterb [fil3[(column-1)*3]][((src[column] & 31) << 3)]; fil[(column-1)*3+1] = voodoo->thefilterg [fil3[(column-1)*3+1]][(((src[column] >> 5) & 63) << 2)]; fil[(column-1)*3+2] = voodoo->thefilter [fil3[(column-1)*3+2]][(((src[column] >> 11) & 31) << 3)]; fil3[(column-1)*3] = voodoo->thefilterb [fil[(column-1)*3]][((src[column] & 31) << 3)]; fil3[(column-1)*3+1] = voodoo->thefilterg [fil[(column-1)*3+1]][(((src[column] >> 5) & 63) << 2)]; fil3[(column-1)*3+2] = voodoo->thefilter [fil[(column-1)*3+2]][(((src[column] >> 11) & 31) << 3)]; } void voodoo_callback(void *p) { voodoo_t *voodoo = (voodoo_t *)p; int y_add = (enable_overscan && !suppress_overscan) ? 16 : 0; int x_add = (enable_overscan && !suppress_overscan) ? 8 : 0; if (voodoo->fbiInit0 & FBIINIT0_VGA_PASS) { if (voodoo->line < voodoo->v_disp) { if (voodoo->dirty_line[voodoo->line]) { uint32_t *p = &((uint32_t *)buffer32->line[voodoo->line + y_add])[32 + x_add]; uint16_t *src = (uint16_t *)&voodoo->fb_mem[voodoo->front_offset + voodoo->line*voodoo->row_width]; int x; voodoo->dirty_line[voodoo->line] = 0; if (voodoo->line < voodoo->dirty_line_low) { voodoo->dirty_line_low = voodoo->line; video_wait_for_buffer(); } if (voodoo->line > voodoo->dirty_line_high) voodoo->dirty_line_high = voodoo->line; if (voodoo->scrfilter && voodoo->scrfilterEnabled) { int j, offset; uint8_t fil[(voodoo->h_disp) * 3]; /* interleaved 24-bit RGB */ if (voodoo->type == VOODOO_2) voodoo_filterline_v2(voodoo, fil, voodoo->h_disp, src, voodoo->line); else voodoo_filterline_v1(voodoo, fil, voodoo->h_disp, src, voodoo->line); for (x = 0; x < voodoo->h_disp; x++) { p[x] = (voodoo->clutData256[fil[x*3]].b << 0 | voodoo->clutData256[fil[x*3+1]].g << 8 | voodoo->clutData256[fil[x*3+2]].r << 16); } } else { for (x = 0; x < voodoo->h_disp; x++) { p[x] = voodoo->video_16to32[src[x]]; } } } } } if (voodoo->line == voodoo->v_disp) { // pclog("retrace %i %i %08x %i\n", voodoo->retrace_count, voodoo->swap_interval, voodoo->swap_offset, voodoo->swap_pending); voodoo->retrace_count++; if (voodoo->swap_pending && (voodoo->retrace_count > voodoo->swap_interval)) { memset(voodoo->dirty_line, 1, 1024); voodoo->retrace_count = 0; voodoo->front_offset = voodoo->swap_offset; voodoo->swap_count--; voodoo->swap_pending = 0; thread_set_event(voodoo->wake_fifo_thread); voodoo->frame_count++; } voodoo->v_retrace = 1; } voodoo->line++; if (voodoo->fbiInit0 & FBIINIT0_VGA_PASS) { if (voodoo->line == voodoo->v_disp) { if (voodoo->dirty_line_high > voodoo->dirty_line_low) svga_doblit(0, voodoo->v_disp, voodoo->h_disp, voodoo->v_disp-1, voodoo->svga); if (voodoo->clutData_dirty) { voodoo->clutData_dirty = 0; voodoo_calc_clutData(voodoo); } voodoo->dirty_line_high = -1; voodoo->dirty_line_low = 2000; } } if (voodoo->line >= voodoo->v_total) { voodoo->line = 0; voodoo->v_retrace = 0; } if (voodoo->line_time) voodoo->timer_count += voodoo->line_time; else voodoo->timer_count += TIMER_USEC * 32; } static void voodoo_add_status_info(char *s, int max_len, void *p) { voodoo_t *voodoo = (voodoo_t *)p; char temps[256]; int pixel_count_current[2]; int pixel_count_total; int texel_count_current[2]; int texel_count_total; uint64_t new_time = timer_read(); uint64_t status_diff = new_time - status_time; status_time = new_time; if (!status_diff) status_diff = 1; svga_add_status_info(s, max_len, &voodoo->svga); pixel_count_current[0] = voodoo->pixel_count[0]; pixel_count_current[1] = voodoo->pixel_count[1]; pixel_count_total = (pixel_count_current[0] + pixel_count_current[1]) - (voodoo->pixel_count_old[0] + voodoo->pixel_count_old[1]); texel_count_current[0] = voodoo->texel_count[0]; texel_count_current[1] = voodoo->texel_count[1]; texel_count_total = (texel_count_current[0] + texel_count_current[1]) - (voodoo->texel_count_old[0] + voodoo->texel_count_old[1]); sprintf(temps, "%f Mpixels/sec (%f)\n%f Mtexels/sec (%f)\n%f ktris/sec\n%f%% CPU (%f%% real)\n%d frames/sec (%i)\n%f%% CPU (%f%% real)\n%f%% CPU (%f%% real)\n"/*%d reads/sec\n%d write/sec\n%d tex/sec\n*/, (double)pixel_count_total/1000000.0, ((double)pixel_count_total/1000000.0) / ((double)voodoo_render_time[0] / status_diff), (double)texel_count_total/1000000.0, ((double)texel_count_total/1000000.0) / ((double)voodoo_render_time[0] / status_diff), (double)voodoo->tri_count/1000.0, ((double)voodoo_time * 100.0) / timer_freq, ((double)voodoo_time * 100.0) / status_diff, voodoo->frame_count, voodoo_recomp, ((double)voodoo_render_time[0] * 100.0) / timer_freq, ((double)voodoo_render_time[0] * 100.0) / status_diff, ((double)voodoo_render_time[1] * 100.0) / timer_freq, ((double)voodoo_render_time[1] * 100.0) / status_diff); strncat(s, temps, max_len); voodoo->pixel_count_old[0] = pixel_count_current[0]; voodoo->pixel_count_old[1] = pixel_count_current[1]; voodoo->texel_count_old[0] = texel_count_current[0]; voodoo->texel_count_old[1] = texel_count_current[1]; voodoo->tri_count = voodoo->frame_count = 0; voodoo->rd_count = voodoo->wr_count = voodoo->tex_count = 0; voodoo_time = 0; voodoo_render_time[0] = voodoo_render_time[1] = 0; voodoo_recomp = 0; } static void voodoo_speed_changed(void *p) { voodoo_t *voodoo = (voodoo_t *)p; voodoo_pixelclock_update(voodoo); voodoo->read_time = pci_nonburst_time + pci_burst_time * ((voodoo->fbiInit4 & 1) ? 2 : 1); 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); // pclog("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); } void *voodoo_init() { int c; voodoo_t *voodoo = malloc(sizeof(voodoo_t)); memset(voodoo, 0, sizeof(voodoo_t)); voodoo->bilinear_enabled = device_get_config_int("bilinear"); 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; } if (voodoo->type == VOODOO_2) /*generate filter lookup tables*/ voodoo_generate_filter_v2(voodoo); else voodoo_generate_filter_v1(voodoo); pci_add(voodoo_pci_read, voodoo_pci_write, voodoo); 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 + 128*128 + 64*64 + 32*32 + 16*16 + 8*8 + 4*4 + 2*2 + 1*1) * 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 + 128*128 + 64*64 + 32*32 + 16*16 + 8*8 + 4*4 + 2*2 + 1*1) * 4); voodoo->texture_cache[1][c].base = -1; /*invalid*/ voodoo->texture_cache[1][c].refcount = 0; } } timer_add(voodoo_callback, &voodoo->timer_count, TIMER_ALWAYS_ENABLED, voodoo); 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_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->fifo_thread = thread_create(fifo_thread, voodoo); voodoo->render_thread[0] = thread_create(render_thread_1, voodoo); if (voodoo->render_threads == 2) voodoo->render_thread[1] = thread_create(render_thread_2, voodoo); timer_add(voodoo_wake_timer, &voodoo->wake_timer, &voodoo->wake_timer, (void *)voodoo); 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; return voodoo; } void voodoo_close(void *p) { FILE *f; voodoo_t *voodoo = (voodoo_t *)p; int c; #ifndef RELEASE_BUILD f = romfopen("texram.dmp", "wb"); fwrite(voodoo->tex_mem[0], voodoo->texture_size*1024*1024, 1, f); fclose(f); if (voodoo->dual_tmus) { f = romfopen("texram2.dmp", "wb"); fwrite(voodoo->tex_mem[1], voodoo->texture_size*1024*1024, 1, f); fclose(f); } #endif thread_kill(voodoo->fifo_thread); thread_kill(voodoo->render_thread[0]); if (voodoo->render_threads == 2) thread_kill(voodoo->render_thread[1]); 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 (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 free(voodoo->fb_mem); if (voodoo->dual_tmus) free(voodoo->tex_mem[1]); free(voodoo->tex_mem[0]); free(voodoo); } static device_config_t voodoo_config[] = { { .name = "type", .description = "Voodoo type", .type = CONFIG_SELECTION, .selection = { { .description = "Voodoo Graphics", .value = VOODOO_1 }, { .description = "Obsidian SB50 + Amethyst (2 TMUs)", .value = VOODOO_SB50 }, { .description = "Voodoo 2", .value = VOODOO_2 }, { .description = "" } }, .default_int = 0 }, { .name = "framebuffer_memory", .description = "Framebuffer memory size", .type = CONFIG_SELECTION, .selection = { { .description = "2 MB", .value = 2 }, { .description = "4 MB", .value = 4 }, { .description = "" } }, .default_int = 2 }, { .name = "texture_memory", .description = "Texture memory size", .type = CONFIG_SELECTION, .selection = { { .description = "2 MB", .value = 2 }, { .description = "4 MB", .value = 4 }, { .description = "" } }, .default_int = 2 }, { .name = "bilinear", .description = "Bilinear filtering", .type = CONFIG_BINARY, .default_int = 1 }, { .name = "dacfilter", .description = "Screen Filter", .type = CONFIG_BINARY, .default_int = 0 }, { .name = "render_threads", .description = "Render threads", .type = CONFIG_SELECTION, .selection = { { .description = "1", .value = 1 }, { .description = "2", .value = 2 }, { .description = "" } }, .default_int = 2 }, #ifndef NO_CODEGEN { .name = "recompiler", .description = "Recompiler", .type = CONFIG_BINARY, .default_int = 1 }, #endif { .type = -1 } }; device_t voodoo_device = { "3DFX Voodoo Graphics", 0, voodoo_init, voodoo_close, NULL, voodoo_speed_changed, NULL, voodoo_add_status_info, voodoo_config };