/* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM * PC systems and compatibles from 1981 through fairly recent * system designs based on the PCI bus. * * This file is part of the 86Box distribution. * * S3 ViRGE emulation. * * * * Authors: Sarah Walker, * Miran Grca, * * Copyright 2008-2018 Sarah Walker. * Copyright 2016-2018 Miran Grca. */ #include #include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/io.h> #include <86box/timer.h> #include <86box/dma.h> #include <86box/mem.h> #include <86box/pci.h> #include <86box/rom.h> #include <86box/device.h> #include <86box/plat.h> #include <86box/thread.h> #include <86box/video.h> #include <86box/i2c.h> #include <86box/vid_ddc.h> #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> #ifdef MIN #undef MIN #endif #ifdef MAX #undef MAX #endif #ifdef CLAMP #undef CLAMP #endif static uint64_t virge_time = 0; static int dither[4][4] = { {0, 4, 1, 5}, {6, 2, 7, 3}, {1, 5, 0, 4}, {7, 3, 6, 2} }; #define ROM_VIRGE_325 "roms/video/s3virge/86c325.bin" #define ROM_DIAMOND_STEALTH3D_2000 "roms/video/s3virge/s3virge.bin" #define ROM_DIAMOND_STEALTH3D_3000 "roms/video/s3virge/diamondstealth3000.vbi" #define ROM_STB_VELOCITY_3D "roms/video/s3virge/stb_velocity3d_110.BIN" #define ROM_VIRGE_DX "roms/video/s3virge/86c375_1.bin" #define ROM_DIAMOND_STEALTH3D_2000PRO "roms/video/s3virge/virgedxdiamond.vbi" #define ROM_VIRGE_GX "roms/video/s3virge/86c375_4.bin" #define ROM_VIRGE_GX2 "roms/video/s3virge/flagpoint.VBI" #define ROM_DIAMOND_STEALTH3D_4000 "roms/video/s3virge/86c357.bin" #define ROM_TRIO3D2X "roms/video/s3virge/TRIO3D2X_8mbsdr.VBI" #define RB_SIZE 256 #define RB_MASK (RB_SIZE - 1) #define RB_ENTRIES (virge->s3d_write_idx - virge->s3d_read_idx) #define RB_FULL (RB_ENTRIES == RB_SIZE) #define RB_EMPTY (!RB_ENTRIES) #define FIFO_SIZE 65536 #define FIFO_MASK (FIFO_SIZE - 1) #define FIFO_ENTRY_SIZE (1 << 31) #define FIFO_ENTRIES (virge->fifo_write_idx - virge->fifo_read_idx) #define FIFO_FULL ((virge->fifo_write_idx - virge->fifo_read_idx) >= FIFO_SIZE) #define FIFO_EMPTY (virge->fifo_read_idx == virge->fifo_write_idx) #define FIFO_TYPE 0xff000000 #define FIFO_ADDR 0x00ffffff enum { S3_VIRGE_325, S3_DIAMOND_STEALTH3D_2000, S3_DIAMOND_STEALTH3D_3000, S3_STB_VELOCITY_3D, S3_VIRGE_DX, S3_DIAMOND_STEALTH3D_2000PRO, S3_VIRGE_GX, S3_VIRGE_GX2, S3_DIAMOND_STEALTH3D_4000, S3_TRIO_3D2X }; enum { S3_VIRGE, S3_VIRGEVX, S3_VIRGEDX, S3_VIRGEGX2, S3_TRIO3D2X }; enum { FIFO_INVALID = (0x00 << 24), FIFO_WRITE_BYTE = (0x01 << 24), FIFO_WRITE_WORD = (0x02 << 24), FIFO_WRITE_DWORD = (0x03 << 24) }; typedef struct { uint32_t addr_type; uint32_t val; } fifo_entry_t; typedef struct s3d_t { uint32_t cmd_set; int clip_l; int clip_r; int clip_t; int clip_b; uint32_t dest_base; uint32_t dest_str; uint32_t z_base; uint32_t z_str; uint32_t tex_base; uint32_t tex_bdr_clr; uint32_t tbv; uint32_t tbu; int32_t TdVdX; int32_t TdUdX; int32_t TdVdY; int32_t TdUdY; uint32_t tus; uint32_t tvs; int32_t TdZdX; int32_t TdZdY; uint32_t tzs; int32_t TdWdX; int32_t TdWdY; uint32_t tws; int32_t TdDdX; int32_t TdDdY; uint32_t tds; int16_t TdGdX; int16_t TdBdX; int16_t TdRdX; int16_t TdAdX; int16_t TdGdY; int16_t TdBdY; int16_t TdRdY; int16_t TdAdY; uint32_t tgs; uint32_t tbs; uint32_t trs; uint32_t tas; uint32_t TdXdY12; uint32_t txend12; uint32_t TdXdY01; uint32_t txend01; uint32_t TdXdY02; uint32_t txs; uint32_t tys; int ty01; int ty12; int tlr; uint8_t fog_r; uint8_t fog_g; uint8_t fog_b; } s3d_t; typedef struct virge_t { mem_mapping_t linear_mapping; mem_mapping_t mmio_mapping; mem_mapping_t new_mmio_mapping; rom_t bios_rom; svga_t svga; uint8_t bank; uint8_t ma_ext; uint8_t virge_id; uint8_t virge_id_high; uint8_t virge_id_low; uint8_t virge_rev; uint32_t linear_base; uint32_t linear_size; uint8_t pci_regs[256]; uint8_t pci_slot; int chip; int bilinear_enabled; int dithering_enabled; int memory_size; int pixel_count; int tri_count; thread_t * render_thread; event_t * wake_render_thread; event_t * wake_main_thread; event_t * not_full_event; uint32_t hwc_fg_col; uint32_t hwc_bg_col; int hwc_col_stack_pos; struct { uint32_t src_base; uint32_t dest_base; int clip_l; int clip_r; int clip_t; int clip_b; int dest_str; int src_str; uint32_t mono_pat_0; uint32_t mono_pat_1; uint32_t pat_bg_clr; uint32_t pat_fg_clr; uint32_t src_bg_clr; uint32_t src_fg_clr; uint32_t cmd_set; int r_width; int r_height; int rsrc_x; int rsrc_y; int rdest_x; int rdest_y; int lxend0; int lxend1; int32_t ldx; uint32_t lxstart; uint32_t lystart; int lycnt; int line_dir; int src_x; int src_y; int dest_x; int dest_y; int w; int h; uint8_t rop; int data_left_count; uint32_t data_left; uint32_t pattern_8[8 * 8]; uint32_t pattern_16[8 * 8]; uint32_t pattern_24[8 * 8]; uint32_t pattern_32[8 * 8]; uint32_t prdx; uint32_t prxstart; uint32_t pldx; uint32_t plxstart; uint32_t pystart; uint32_t pycnt; uint32_t dest_l, dest_r; } s3d; s3d_t s3d_tri; s3d_t s3d_buffer[RB_SIZE]; int s3d_read_idx; int s3d_write_idx; int s3d_busy; struct { uint32_t pri_ctrl; uint32_t chroma_ctrl; uint32_t sec_ctrl; uint32_t chroma_upper_bound; uint32_t sec_filter; uint32_t blend_ctrl; uint32_t pri_fb0, pri_fb1; uint32_t pri_stride; uint32_t buffer_ctrl; uint32_t sec_fb0, sec_fb1; uint32_t sec_stride; uint32_t overlay_ctrl; int32_t k1_vert_scale; int32_t k2_vert_scale; int32_t dda_vert_accumulator; int32_t k1_horiz_scale; int32_t k2_horiz_scale; int32_t dda_horiz_accumulator; uint32_t fifo_ctrl; uint32_t pri_start; uint32_t pri_size; uint32_t sec_start; uint32_t sec_size; int sdif; int pri_x; int pri_y; int pri_w; int pri_h; int sec_x; int sec_y; int sec_w; int sec_h; } streams; fifo_entry_t fifo[FIFO_SIZE]; volatile int fifo_read_idx, fifo_write_idx; volatile int fifo_thread_run, render_thread_run; thread_t * fifo_thread; event_t *wake_fifo_thread; event_t * fifo_not_full_event; int virge_busy; uint8_t subsys_stat; uint8_t subsys_cntl; int local; uint8_t serialport; uint8_t irq_state; uint8_t advfunc_cntl; void *i2c, *ddc; int onboard; int fifo_slots_num; uint32_t vram_mask; uint8_t reg6b; uint8_t lfb_bios; uint8_t int_line; uint8_t cmd_dma; uint32_t cmd_dma_base; uint32_t dma_ptr; int pci; int is_agp; } virge_t; static __inline void wake_fifo_thread(virge_t *virge) { /* Wake up FIFO thread if moving from idle */ thread_set_event(virge->wake_fifo_thread); } static virge_t *reset_state = NULL; static video_timings_t timing_diamond_stealth3d_2000_pci = { .type = VIDEO_PCI, .write_b = 2, .write_w = 2, .write_l = 3, .read_b = 28, .read_w = 28, .read_l = 45 }; static video_timings_t timing_diamond_stealth3d_3000_pci = { .type = VIDEO_PCI, .write_b = 2, .write_w = 2, .write_l = 4, .read_b = 26, .read_w = 26, .read_l = 42 }; static video_timings_t timing_virge_dx_pci = { .type = VIDEO_PCI, .write_b = 2, .write_w = 2, .write_l = 3, .read_b = 28, .read_w = 28, .read_l = 45 }; static video_timings_t timing_virge_agp = { .type = VIDEO_AGP, .write_b = 2, .write_w = 2, .write_l = 3, .read_b = 28, .read_w = 28, .read_l = 45 }; static void queue_triangle(virge_t *virge); static void s3_virge_recalctimings(svga_t *svga); static void s3_virge_updatemapping(virge_t *virge); static void s3_virge_bitblt(virge_t *virge, int count, uint32_t cpu_dat); static uint8_t s3_virge_mmio_read(uint32_t addr, void *priv); static uint16_t s3_virge_mmio_read_w(uint32_t addr, void *priv); static uint32_t s3_virge_mmio_read_l(uint32_t addr, void *priv); static void s3_virge_mmio_write(uint32_t addr, uint8_t val, void *priv); static void s3_virge_mmio_write_w(uint32_t addr, uint16_t val, void *priv); static void s3_virge_mmio_write_l(uint32_t addr, uint32_t val, void *priv); enum { CMD_SET_AE = 1, CMD_SET_HC = (1 << 1), CMD_SET_FORMAT_MASK = (7 << 2), CMD_SET_FORMAT_8 = (0 << 2), CMD_SET_FORMAT_16 = (1 << 2), CMD_SET_FORMAT_24 = (2 << 2), CMD_SET_MS = (1 << 6), CMD_SET_IDS = (1 << 7), CMD_SET_MP = (1 << 8), CMD_SET_TP = (1 << 9), CMD_SET_ITA_MASK = (3 << 10), CMD_SET_ITA_BYTE = (0 << 10), CMD_SET_ITA_WORD = (1 << 10), CMD_SET_ITA_DWORD = (2 << 10), CMD_SET_ZUP = (1 << 23), CMD_SET_ZB_MODE = (3 << 24), CMD_SET_XP = (1 << 25), CMD_SET_YP = (1 << 26), CMD_SET_COMMAND_MASK = (15 << 27) }; #define CMD_SET_FE (1 << 17) #define CMD_SET_ABC_SRC (1 << 18) #define CMD_SET_ABC_ENABLE (1 << 19) #define CMD_SET_TWE (1 << 26) enum { CMD_SET_COMMAND_BITBLT = (0 << 27), CMD_SET_COMMAND_RECTFILL = (2 << 27), CMD_SET_COMMAND_LINE = (3 << 27), CMD_SET_COMMAND_POLY = (5 << 27), CMD_SET_COMMAND_NOP = (15 << 27) }; #define INT_VSY (1 << 0) #define INT_S3D_DONE (1 << 1) #define INT_FIFO_OVF (1 << 2) #define INT_FIFO_EMP (1 << 3) #define INT_3DF_EMP (1 << 6) #define INT_MASK 0xff #define SERIAL_PORT_SCW (1 << 0) #define SERIAL_PORT_SDW (1 << 1) #define SERIAL_PORT_SCR (1 << 2) #define SERIAL_PORT_SDR (1 << 3) static void s3_virge_update_irqs(virge_t *virge) { if ((virge->svga.crtc[0x32] & 0x10) && (virge->subsys_stat & virge->subsys_cntl & INT_MASK)) pci_set_irq(virge->pci_slot, PCI_INTA, &virge->irq_state); else pci_clear_irq(virge->pci_slot, PCI_INTA, &virge->irq_state); } static void s3_virge_out(uint16_t addr, uint8_t val, void *priv) { virge_t *virge = (virge_t *) priv; svga_t * svga = &virge->svga; uint8_t old; uint32_t cursoraddr; if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1)) addr ^= 0x60; switch (addr) { case 0x3c5: if (svga->seqaddr >= 0x10) { svga->seqregs[svga->seqaddr & 0x1f] = val; svga_recalctimings(svga); return; } if (svga->seqaddr == 4) { /*Chain-4 - update banking*/ if (val & 8) svga->write_bank = svga->read_bank = virge->bank << 16; else svga->write_bank = svga->read_bank = virge->bank << 14; } else if (svga->seqaddr == 0x08) { svga->seqregs[svga->seqaddr] = val & 0x0f; return; } else if ((svga->seqaddr == 0x0d) && (svga->seqregs[0x08] == 0x06)) { svga->seqregs[svga->seqaddr] = val; svga->dpms = (svga->seqregs[0x0d] & 0x50) || (svga->crtc[0x56] & 0x06); svga_recalctimings(svga); return; } break; case 0x3d4: svga->crtcreg = val; return; case 0x3d5: if ((svga->crtcreg < 7) && (svga->crtc[0x11] & 0x80)) return; if ((svga->crtcreg == 7) && (svga->crtc[0x11] & 0x80)) val = (svga->crtc[7] & ~0x10) | (val & 0x10); if ((svga->crtcreg >= 0x20) && (svga->crtcreg < 0x40) && (svga->crtcreg != 0x36) && (svga->crtcreg != 0x38) && (svga->crtcreg != 0x39) && ((svga->crtc[0x38] & 0xcc) != 0x48)) return; if ((svga->crtcreg >= 0x40) && ((svga->crtc[0x39] & 0xe0) != 0xa0)) return; if ((svga->crtcreg == 0x36) && (svga->crtc[0x39] != 0xa5)) return; if (svga->crtcreg >= 0x80) return; old = svga->crtc[svga->crtcreg]; svga->crtc[svga->crtcreg] = val; switch (svga->crtcreg) { case 0x31: virge->ma_ext = (virge->ma_ext & 0x1c) | ((val & 0x30) >> 4); break; case 0x32: s3_virge_update_irqs(virge); break; case 0x69: virge->ma_ext = val & 0x1f; break; case 0x35: virge->bank = (virge->bank & 0x70) | (val & 0xf); if (svga->chain4) svga->write_bank = svga->read_bank = virge->bank << 16; else svga->write_bank = svga->read_bank = virge->bank << 14; break; case 0x51: virge->bank = (virge->bank & 0x4f) | ((val & 0xc) << 2); if (svga->chain4) svga->write_bank = svga->read_bank = virge->bank << 16; else svga->write_bank = svga->read_bank = virge->bank << 14; virge->ma_ext = (virge->ma_ext & ~0xc) | ((val & 3) << 2); break; case 0x6a: virge->bank = val; if (svga->chain4) svga->write_bank = svga->read_bank = virge->bank << 16; else svga->write_bank = svga->read_bank = virge->bank << 14; break; case 0x3a: if (val & 0x10) svga->gdcreg[5] |= 0x40; /*Horrible cheat*/ break; case 0x45: svga->hwcursor.ena = val & 1; break; case 0x46: case 0x47: case 0x48: case 0x49: case 0x4c: case 0x4d: case 0x4e: case 0x4f: svga->hwcursor.x = ((svga->crtc[0x46] << 8) | svga->crtc[0x47]) & 0x7ff; svga->hwcursor.y = ((svga->crtc[0x48] << 8) | svga->crtc[0x49]) & 0x7ff; svga->hwcursor.xoff = svga->crtc[0x4e] & 0x3f; svga->hwcursor.yoff = svga->crtc[0x4f] & 0x3f; cursoraddr = (virge->memory_size == 8) ? 0x1fff : 0x0fff; svga->hwcursor.addr = ((((svga->crtc[0x4c] << 8) | svga->crtc[0x4d]) & cursoraddr) * 1024) + (svga->hwcursor.yoff * 16); break; case 0x4a: switch (virge->hwc_col_stack_pos) { case 0: virge->hwc_fg_col = (virge->hwc_fg_col & 0xffff00) | val; break; case 1: virge->hwc_fg_col = (virge->hwc_fg_col & 0xff00ff) | (val << 8); break; case 2: virge->hwc_fg_col = (virge->hwc_fg_col & 0x00ffff) | (val << 16); break; default: break; } virge->hwc_col_stack_pos = (virge->hwc_col_stack_pos + 1) & 3; break; case 0x4b: switch (virge->hwc_col_stack_pos) { case 0: virge->hwc_bg_col = (virge->hwc_bg_col & 0xffff00) | val; break; case 1: virge->hwc_bg_col = (virge->hwc_bg_col & 0xff00ff) | (val << 8); break; case 2: virge->hwc_bg_col = (virge->hwc_bg_col & 0x00ffff) | (val << 16); break; default: break; } virge->hwc_col_stack_pos = (virge->hwc_col_stack_pos + 1) & 3; break; case 0x53: case 0x58: case 0x59: case 0x5a: s3_virge_updatemapping(virge); break; case 0x56: svga->dpms = (svga->seqregs[0x0d] & 0x50) || (svga->crtc[0x56] & 0x06); old = ~val; /* force recalc */ break; case 0x5c: if ((val & 0xa0) == 0x80) i2c_gpio_set(virge->i2c, !!(val & 0x40), !!(val & 0x10)); break; case 0x67: switch (val >> 4) { case 2: case 3: svga->bpp = 15; break; case 4: case 5: svga->bpp = 16; break; case 7: svga->bpp = 24; break; case 13: svga->bpp = (virge->chip == S3_VIRGEVX) ? 24 : 32; break; default: svga->bpp = 8; break; } break; case 0xaa: i2c_gpio_set(virge->i2c, !!(val & SERIAL_PORT_SCW), !!(val & SERIAL_PORT_SDW)); break; default: break; } if (old != val) { if (svga->crtcreg < 0xe || svga->crtcreg > 0x10) { if ((svga->crtcreg == 0xc) || (svga->crtcreg == 0xd)) { svga->fullchange = 3; svga->ma_latch = ((svga->crtc[0xc] << 8) | svga->crtc[0xd]) + ((svga->crtc[8] & 0x60) >> 5); if ((svga->crtc[0x67] & 0xc) != 0xc) svga->ma_latch |= (virge->ma_ext << 16); } else { svga->fullchange = changeframecount; svga_recalctimings(svga); } } } break; default: break; } svga_out(addr, val, svga); } static uint8_t s3_virge_in(uint16_t addr, void *priv) { virge_t *virge = (virge_t *) priv; svga_t * svga = &virge->svga; uint8_t ret; if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1)) addr ^= 0x60; switch (addr) { case 0x3c1: if (svga->attraddr > 0x14) ret = 0xff; else ret = svga_in(addr, svga); break; case 0x3c5: if (svga->seqaddr >= 8) ret = svga->seqregs[svga->seqaddr & 0x1f]; else if (svga->seqaddr <= 4) ret = svga_in(addr, svga); else ret = 0xff; break; case 0x3d4: ret = svga->crtcreg; break; case 0x3d5: switch (svga->crtcreg) { case 0x2d: ret = virge->virge_id_high; break; /*Extended chip ID*/ case 0x2e: ret = virge->virge_id_low; break; /*New chip ID*/ case 0x2f: ret = virge->virge_rev; break; case 0x30: ret = virge->virge_id; break; /*Chip ID*/ case 0x31: ret = (svga->crtc[0x31] & 0xcf) | ((virge->ma_ext & 3) << 4); break; case 0x33: ret = (svga->crtc[0x33] | 0x04); break; case 0x35: ret = (svga->crtc[0x35] & 0xf0) | (virge->bank & 0xf); break; case 0x36: ret = (svga->crtc[0x36] & 0xfc) | 2; break; /*PCI bus*/ case 0x45: virge->hwc_col_stack_pos = 0; ret = svga->crtc[0x45]; break; case 0x51: ret = (svga->crtc[0x51] & 0xf0) | ((virge->bank >> 2) & 0xc) | ((virge->ma_ext >> 2) & 3); break; case 0x5c: /* General Output Port Register */ ret = svga->crtc[svga->crtcreg] & 0xa0; if (((svga->miscout >> 2) & 3) == 3) ret |= svga->crtc[0x42] & 0x0f; else ret |= ((svga->miscout >> 2) & 3); if ((ret & 0xa0) == 0xa0) { if ((svga->crtc[0x5c] & 0x40) && i2c_gpio_get_scl(virge->i2c)) ret |= 0x40; if ((svga->crtc[0x5c] & 0x10) && i2c_gpio_get_sda(virge->i2c)) ret |= 0x10; } break; case 0x69: ret = virge->ma_ext; break; case 0x6a: ret = virge->bank; break; case 0xaa: /* DDC */ if (virge->chip >= S3_VIRGEGX2) { ret = svga->crtc[0xaa] & ~(SERIAL_PORT_SCR | SERIAL_PORT_SDR); if ((svga->crtc[0xaa] & SERIAL_PORT_SCW) && i2c_gpio_get_scl(virge->i2c)) ret |= SERIAL_PORT_SCR; if ((svga->crtc[0xaa] & SERIAL_PORT_SDW) && i2c_gpio_get_sda(virge->i2c)) ret |= SERIAL_PORT_SDR; break; } else ret = svga->crtc[0xaa]; break; default: ret = svga->crtc[svga->crtcreg]; break; } break; default: ret = svga_in(addr, svga); break; } return ret; } static void s3_virge_recalctimings(svga_t *svga) { virge_t *virge = (virge_t *) svga->priv; svga->hdisp = svga->hdisp_old; if (!svga->scrblank && svga->attr_palette_enable && (svga->crtc[0x43] & 0x80)) { /* TODO: In case of bug reports, disable 9-dots-wide character clocks in graphics modes. */ svga->dots_per_clock = ((svga->seqregs[1] & 1) ? 16 : 18); } if ((svga->crtc[0x33] & 0x20) || ((svga->crtc[0x67] & 0xc) == 0xc)) { /* In this mode, the dots per clock are always 8 or 16, never 9 or 18. */ if (!svga->scrblank && svga->attr_palette_enable) svga->dots_per_clock = (svga->seqregs[1] & 8) ? 16 : 8; } if (svga->crtc[0x5d] & 0x01) svga->htotal |= 0x100; if (svga->crtc[0x5d] & 0x02) { svga->hdisp_time |= 0x100; svga->hdisp |= (0x100 * svga->dots_per_clock); } if (svga->crtc[0x5e] & 0x01) svga->vtotal |= 0x400; if (svga->crtc[0x5e] & 0x02) svga->dispend |= 0x400; if (svga->crtc[0x5e] & 0x04) svga->vblankstart |= 0x400; if (svga->crtc[0x5e] & 0x10) svga->vsyncstart |= 0x400; if (svga->crtc[0x5e] & 0x40) svga->split |= 0x400; svga->interlace = svga->crtc[0x42] & 0x20; if (((svga->miscout >> 2) & 3) == 3) { int n = svga->seqregs[0x12] & 0x1f; int r = (svga->seqregs[0x12] >> 5); if ((virge->chip == S3_VIRGEVX) || (virge->chip == S3_VIRGEDX)) r &= 7; else if (virge->chip >= S3_VIRGEGX2) r &= 10; else r &= 3; int m = svga->seqregs[0x13] & 0x7f; double freq = (((double) m + 2) / (((double) n + 2) * (double) (1 << r))) * 14318184.0; svga->clock = (cpuclock * (float) (1ULL << 32)) / freq; } if ((svga->crtc[0x33] & 0x20) || ((svga->crtc[0x67] & 0xc) == 0xc)) { /* The S3 version of the Cirrus' special blanking mode, with identical behavior. */ svga->hblankstart = (((svga->crtc[0x5d] & 0x02) >> 1) << 8) + svga->crtc[1]/* + ((svga->crtc[3] >> 5) & 3) + 1*/; svga->hblank_end_val = svga->htotal - 1 /* + ((svga->crtc[3] >> 5) & 3)*/; svga->monitor->mon_overscan_y = 0; svga->monitor->mon_overscan_x = 0; /* Also make sure vertical blanking starts on display end. */ svga->vblankstart = svga->dispend; video_force_resize_set_monitor(1, svga->monitor_index); } else { svga->hblankstart = (((svga->crtc[0x5d] & 0x04) >> 2) << 8) + svga->crtc[2]; svga->hblank_end_val = (svga->crtc[3] & 0x1f) | (((svga->crtc[5] & 0x80) >> 7) << 5) | (((svga->crtc[0x5d] & 0x08) >> 3) << 6); svga->hblank_end_mask = 0x7f; video_force_resize_set_monitor(1, svga->monitor_index); } if ((svga->crtc[0x67] & 0xc) != 0xc) { /*VGA mode*/ svga->ma_latch |= (virge->ma_ext << 16); if (svga->crtc[0x51] & 0x30) svga->rowoffset |= (svga->crtc[0x51] & 0x30) << 4; else if (svga->crtc[0x43] & 0x04) svga->rowoffset |= 0x100; if (!svga->rowoffset) svga->rowoffset = 256; svga->lowres = !((svga->gdcreg[5] & 0x40) && (svga->crtc[0x3a] & 0x10)); if ((svga->gdcreg[5] & 0x40) && (svga->crtc[0x3a] & 0x10)) switch (svga->bpp) { case 8: svga->render = svga_render_8bpp_highres; break; case 15: svga->render = svga_render_15bpp_highres; if ((virge->chip != S3_VIRGEVX) && (virge->chip < S3_VIRGEGX2)) { svga->hdisp >>= 1; svga->dots_per_clock >>= 1; } break; case 16: svga->render = svga_render_16bpp_highres; if ((virge->chip != S3_VIRGEVX) && (virge->chip < S3_VIRGEGX2)) { svga->hdisp >>= 1; svga->dots_per_clock >>= 1; } break; case 24: svga->render = svga_render_24bpp_highres; if ((virge->chip != S3_VIRGEVX) && (virge->chip < S3_VIRGEGX2)) svga->rowoffset = (svga->rowoffset * 3) / 4; /*Hack*/ break; case 32: svga->render = svga_render_32bpp_highres; break; default: break; } svga->vram_display_mask = (!(svga->crtc[0x31] & 0x08) && (svga->crtc[0x32] & 0x40)) ? 0x3ffff : virge->vram_mask; svga->overlay.ena = 0; if (virge->chip >= S3_VIRGEGX2 && (svga->crtc[0x67] & 0xc) == 0xc) { /* ViRGE/GX2 and later does not use primary stream registers. */ svga->overlay.x = virge->streams.sec_x; svga->overlay.y = virge->streams.sec_y; svga->overlay.cur_ysize = virge->streams.sec_h; if (virge->streams.buffer_ctrl & 2) svga->overlay.addr = virge->streams.sec_fb1; else svga->overlay.addr = virge->streams.sec_fb0; svga->overlay.ena = (svga->overlay.x >= 0) && !!(virge->streams.blend_ctrl & 0x20); svga->overlay.v_acc = virge->streams.dda_vert_accumulator; svga->rowoffset = virge->streams.pri_stride >> 3; svga->vram_display_mask = virge->vram_mask; } } else { /*Streams mode*/ if (virge->streams.buffer_ctrl & 1) svga->ma_latch = virge->streams.pri_fb1 >> 2; else svga->ma_latch = virge->streams.pri_fb0 >> 2; svga->hdisp = virge->streams.pri_w + 1; if (virge->streams.pri_h < svga->dispend) svga->dispend = virge->streams.pri_h; svga->overlay.x = virge->streams.sec_x - virge->streams.pri_x; svga->overlay.y = virge->streams.sec_y - virge->streams.pri_y; svga->overlay.cur_ysize = virge->streams.sec_h; if (virge->streams.buffer_ctrl & 2) svga->overlay.addr = virge->streams.sec_fb1; else svga->overlay.addr = virge->streams.sec_fb0; svga->overlay.ena = (svga->overlay.x >= 0); svga->overlay.v_acc = virge->streams.dda_vert_accumulator; svga->rowoffset = virge->streams.pri_stride >> 3; if (virge->chip <= S3_VIRGEDX && svga->overlay.ena) { svga->overlay.ena = (((virge->streams.blend_ctrl >> 24) & 7) == 0b000) || (((virge->streams.blend_ctrl >> 24) & 7) == 0b101); } else if (virge->chip == S3_VIRGEGX2 && svga->overlay.ena) { /* 0x20 = Secondary Stream enabled */ /* 0x2000 = Primary Stream enabled */ svga->overlay.ena = !!(virge->streams.blend_ctrl & 0x20); } switch ((virge->streams.pri_ctrl >> 24) & 0x7) { case 0: /*RGB-8 (CLUT)*/ svga->render = svga_render_8bpp_highres; break; case 3: /*KRGB-16 (1.5.5.5)*/ svga->render = svga_render_15bpp_highres; break; case 5: /*RGB-16 (5.6.5)*/ svga->render = svga_render_16bpp_highres; break; case 6: /*RGB-24 (8.8.8)*/ svga->render = svga_render_24bpp_highres; break; case 7: /*XRGB-32 (X.8.8.8)*/ svga->render = svga_render_32bpp_highres; break; } svga->vram_display_mask = virge->vram_mask; } svga->hoverride = 1; if (svga->render == svga_render_2bpp_lowres) svga->render = svga_render_2bpp_s3_lowres; else if (svga->render == svga_render_2bpp_highres) svga->render = svga_render_2bpp_s3_highres; } static void s3_virge_update_buffer(virge_t *virge) { svga_t *svga = &virge->svga; if ((svga->crtc[0x67] & 0xc) != 0xc) return; if (virge->chip < S3_VIRGEGX2) { if (virge->streams.buffer_ctrl & 1) svga->ma_latch = virge->streams.pri_fb1 >> 2; else svga->ma_latch = virge->streams.pri_fb0 >> 2; } if (virge->streams.buffer_ctrl & 2) svga->overlay.addr = virge->streams.sec_fb1; else svga->overlay.addr = virge->streams.sec_fb0; svga->rowoffset = virge->streams.pri_stride >> 3; } static void s3_virge_updatemapping(virge_t *virge) { svga_t *svga = &virge->svga; if (!(virge->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_MEM)) { mem_mapping_disable(&svga->mapping); mem_mapping_disable(&virge->linear_mapping); mem_mapping_disable(&virge->mmio_mapping); mem_mapping_disable(&virge->new_mmio_mapping); return; } switch (svga->gdcreg[6] & 0xc) { /*Banked framebuffer*/ case 0x0: /*128k at A0000*/ mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x20000); svga->banked_mask = 0xffff; break; case 0x4: /*64k at A0000*/ mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x10000); svga->banked_mask = 0xffff; break; case 0x8: /*32k at B0000*/ mem_mapping_set_addr(&svga->mapping, 0xb0000, 0x08000); svga->banked_mask = 0x7fff; break; case 0xC: /*32k at B8000*/ mem_mapping_set_addr(&svga->mapping, 0xb8000, 0x08000); svga->banked_mask = 0x7fff; break; } virge->linear_base = (svga->crtc[0x5a] << 16) | (svga->crtc[0x59] << 24); if ((svga->crtc[0x58] & 0x10) || (virge->advfunc_cntl & 0x10)) { /*Linear framebuffer*/ switch (svga->crtc[0x58] & 3) { case 0: /*64k*/ virge->linear_size = 0x10000; break; case 1: /*1mb*/ virge->linear_size = 0x100000; break; case 2: /*2mb*/ virge->linear_size = 0x200000; break; case 3: /*4mb on other than ViRGE/VX, 8mb on ViRGE/VX*/ if ((virge->chip == S3_VIRGEVX) || (virge->chip == S3_TRIO3D2X)) virge->linear_size = 0x800000; else virge->linear_size = 0x400000; break; case 7: virge->linear_size = 0x800000; break; } virge->linear_base &= ~(virge->linear_size - 1); if (virge->linear_base == 0xa0000) { mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x10000); mem_mapping_disable(&virge->linear_mapping); } else { if ((virge->chip == S3_VIRGEVX) || (virge->chip == S3_TRIO3D2X)) virge->linear_base &= 0xfe000000; else virge->linear_base &= 0xfc000000; mem_mapping_set_addr(&virge->linear_mapping, virge->linear_base, virge->linear_size); } svga->fb_only = 1; } else { mem_mapping_disable(&virge->linear_mapping); svga->fb_only = 0; } if ((svga->crtc[0x53] & 0x10) || (virge->advfunc_cntl & 0x20)) { /*Old MMIO*/ if (svga->crtc[0x53] & 0x20) mem_mapping_set_addr(&virge->mmio_mapping, 0xb8000, 0x8000); else mem_mapping_set_addr(&virge->mmio_mapping, 0xa0000, 0x10000); } else mem_mapping_disable(&virge->mmio_mapping); if (svga->crtc[0x53] & 0x08) /*New MMIO*/ mem_mapping_set_addr(&virge->new_mmio_mapping, virge->linear_base + 0x1000000, 0x10000); else mem_mapping_disable(&virge->new_mmio_mapping); } static void s3_virge_vblank_start(svga_t *svga) { virge_t *virge = (virge_t *) svga->priv; virge->subsys_stat |= INT_VSY; s3_virge_update_irqs(virge); } static void s3_virge_wait_fifo_idle(virge_t *virge) { while (!FIFO_EMPTY) { wake_fifo_thread(virge); thread_wait_event(virge->fifo_not_full_event, 1); } } static uint8_t s3_virge_mmio_read(uint32_t addr, void *priv) { virge_t *virge = (virge_t *) priv; uint8_t ret; switch (addr & 0xffff) { case 0x8504: if (!virge->virge_busy) wake_fifo_thread(virge); ret = virge->subsys_stat; return ret; case 0x8505: ret = 0xc0; if (virge->s3d_busy || virge->virge_busy || !FIFO_EMPTY) ret |= 0x10; else ret |= 0x30; if (!virge->virge_busy) wake_fifo_thread(virge); return ret; case 0x850c: ret = virge->advfunc_cntl & 0x3f; ret |= virge->fifo_slots_num << 6; ret &= 0xff; return ret; case 0x850d: ret = virge->fifo_slots_num >> 2; return ret; case 0x83b0 ... 0x83df: return s3_virge_in(addr & 0x3ff, priv); case 0x859c: return virge->cmd_dma; case 0xff20: case 0xff21: ret = virge->serialport & ~(SERIAL_PORT_SCR | SERIAL_PORT_SDR); if ((virge->serialport & SERIAL_PORT_SCW) && i2c_gpio_get_scl(virge->i2c)) ret |= SERIAL_PORT_SCR; if ((virge->serialport & SERIAL_PORT_SDW) && i2c_gpio_get_sda(virge->i2c)) ret |= SERIAL_PORT_SDR; return ret; } return 0xff; } static uint16_t s3_virge_mmio_read_w(uint32_t addr, void *priv) { virge_t *virge = (virge_t *) priv; uint16_t ret; switch (addr & 0xfffe) { case 0x8504: ret = 0xc000; if (virge->s3d_busy || virge->virge_busy || !FIFO_EMPTY) ret |= 0x1000; else ret |= 0x3000; ret |= virge->subsys_stat; if (!virge->virge_busy) wake_fifo_thread(virge); return ret; case 0x850c: ret = virge->advfunc_cntl & 0x3f; ret |= virge->fifo_slots_num << 6; return ret; case 0x859c: return virge->cmd_dma; default: return s3_virge_mmio_read(addr, priv) | (s3_virge_mmio_read(addr + 1, priv) << 8); } return 0xffff; } static uint32_t s3_virge_mmio_read_l(uint32_t addr, void *priv) { virge_t *virge = (virge_t *) priv; svga_t *svga = &virge->svga; uint32_t ret = 0xffffffff; switch (addr & 0xfffc) { case 0x8180: ret = virge->streams.pri_ctrl; break; case 0x8184: ret = virge->streams.chroma_ctrl; break; case 0x8190: ret = virge->streams.sec_ctrl; break; case 0x8194: ret = virge->streams.chroma_upper_bound; break; case 0x8198: ret = virge->streams.sec_filter; break; case 0x81a0: ret = virge->streams.blend_ctrl; svga_recalctimings(svga); break; case 0x81c0: ret = virge->streams.pri_fb0; s3_virge_update_buffer(virge); svga->fullchange = changeframecount; break; case 0x81c4: ret = virge->streams.pri_fb1; s3_virge_update_buffer(virge); svga->fullchange = changeframecount; break; case 0x81c8: ret = virge->streams.pri_stride; s3_virge_update_buffer(virge); svga->fullchange = changeframecount; break; case 0x81cc: ret = virge->streams.buffer_ctrl; s3_virge_update_buffer(virge); svga->fullchange = changeframecount; break; case 0x81d0: ret = virge->streams.sec_fb0; s3_virge_update_buffer(virge); svga->fullchange = changeframecount; break; case 0x81d4: ret = virge->streams.sec_fb1; s3_virge_update_buffer(virge); svga->fullchange = changeframecount; break; case 0x81d8: ret = virge->streams.sec_stride; s3_virge_update_buffer(virge); svga->fullchange = changeframecount; break; case 0x81dc: ret = virge->streams.overlay_ctrl; svga->fullchange = changeframecount; break; case 0x81e0: ret = virge->streams.k1_vert_scale; break; case 0x81e4: ret = virge->streams.k2_vert_scale; break; case 0x81e8: ret = virge->streams.dda_vert_accumulator; break; case 0x81ec: ret = virge->streams.fifo_ctrl; break; case 0x81f0: ret = virge->streams.pri_start; break; case 0x81f4: ret = virge->streams.pri_size; break; case 0x81f8: ret = virge->streams.sec_start; break; case 0x81fc: ret = virge->streams.sec_size; break; case 0x8504: ret = 0x0000c000; if (virge->s3d_busy || virge->virge_busy || !FIFO_EMPTY) ret |= 0x00001000; else ret |= 0x00003000; ret |= virge->subsys_stat; if (!virge->virge_busy) wake_fifo_thread(virge); break; case 0x850c: ret = virge->advfunc_cntl & 0x3f; ret |= virge->fifo_slots_num << 6; break; case 0x8590: ret = virge->cmd_dma_base; break; case 0x8594: break; case 0x8598: ret = virge->dma_ptr; break; case 0x859c: ret = virge->cmd_dma; break; case 0xa4d4: case 0xa8d4: case 0xacd4: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.src_base; break; case 0xa4d8: case 0xa8d8: case 0xacd8: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.dest_base; break; case 0xa4dc: case 0xa8dc: case 0xacdc: s3_virge_wait_fifo_idle(virge); ret = (virge->s3d.clip_l << 16) | virge->s3d.clip_r; break; case 0xa4e0: case 0xa8e0: case 0xace0: s3_virge_wait_fifo_idle(virge); ret = (virge->s3d.clip_t << 16) | virge->s3d.clip_b; break; case 0xa4e4: case 0xa8e4: case 0xace4: s3_virge_wait_fifo_idle(virge); ret = (virge->s3d.dest_str << 16) | virge->s3d.src_str; break; case 0xa4e8: case 0xace8: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.mono_pat_0; break; case 0xa4ec: case 0xacec: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.mono_pat_1; break; case 0xa4f0: case 0xacf0: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.pat_bg_clr; break; case 0xa4f4: case 0xa8f4: case 0xacf4: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.pat_fg_clr; break; case 0xa4f8: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.src_bg_clr; break; case 0xa4fc: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.src_fg_clr; break; case 0xa500: case 0xa900: case 0xad00: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.cmd_set; break; case 0xa504: s3_virge_wait_fifo_idle(virge); ret = (virge->s3d.r_width << 16) | virge->s3d.r_height; break; case 0xa508: s3_virge_wait_fifo_idle(virge); ret = (virge->s3d.rsrc_x << 16) | virge->s3d.rsrc_y; break; case 0xa50c: s3_virge_wait_fifo_idle(virge); ret = (virge->s3d.rdest_x << 16) | virge->s3d.rdest_y; break; case 0xa96c: s3_virge_wait_fifo_idle(virge); ret = (virge->s3d.lxend0 << 16) | virge->s3d.lxend1; break; case 0xa970: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.ldx; break; case 0xa974: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.lxstart; break; case 0xa978: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.lystart; break; case 0xa97c: s3_virge_wait_fifo_idle(virge); ret = (virge->s3d.line_dir << 31) | virge->s3d.lycnt; break; case 0xad68: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.prdx; break; case 0xad6c: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.prxstart; break; case 0xad70: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.pldx; break; case 0xad74: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.plxstart; break; case 0xad78: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.pystart; break; case 0xad7c: s3_virge_wait_fifo_idle(virge); ret = virge->s3d.pycnt; break; default: ret = s3_virge_mmio_read_w(addr, priv) | (s3_virge_mmio_read_w(addr + 2, priv) << 16); } return ret; } static void fifo_thread(void *param) { virge_t *virge = (virge_t *)param; while (virge->fifo_thread_run) { thread_set_event(virge->fifo_not_full_event); thread_wait_event(virge->wake_fifo_thread, -1); thread_reset_event(virge->wake_fifo_thread); virge->virge_busy = 1; while (!FIFO_EMPTY) { uint64_t start_time = plat_timer_read(); uint64_t end_time; fifo_entry_t *fifo = &virge->fifo[virge->fifo_read_idx & FIFO_MASK]; uint32_t val = fifo->val; switch (fifo->addr_type & FIFO_TYPE) { case FIFO_WRITE_BYTE: if (((fifo->addr_type & FIFO_ADDR) & 0xfffc) < 0x8000) s3_virge_bitblt(virge, 8, val); else if (((fifo->addr_type & FIFO_ADDR) & 0xffff) == 0x859c) virge->cmd_dma = val; break; case FIFO_WRITE_WORD: if (((fifo->addr_type & FIFO_ADDR) & 0xfffc) < 0x8000) { if (virge->s3d.cmd_set & CMD_SET_MS) s3_virge_bitblt(virge, 16, ((val >> 8) | (val << 8)) << 16); else s3_virge_bitblt(virge, 16, val); } else if (((fifo->addr_type & FIFO_ADDR) & 0xfffe) == 0x859c) virge->cmd_dma = val; break; case FIFO_WRITE_DWORD: if (((fifo->addr_type & FIFO_ADDR) & 0xfffc) < 0x8000) { if (virge->s3d.cmd_set & CMD_SET_MS) s3_virge_bitblt(virge, 32, ((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24)); else s3_virge_bitblt(virge, 32, val); } else { switch ((fifo->addr_type & FIFO_ADDR) & 0xfffc) { case 0x8590: virge->cmd_dma_base = val; break; case 0x8594: virge->dma_ptr = val; break; case 0x8598: break; case 0x859c: virge->cmd_dma = val; break; case 0xa000 ... 0xa1fc: { int x = (fifo->addr_type & FIFO_ADDR) & 4; int y = ((fifo->addr_type & FIFO_ADDR) >> 3) & 7; int color; int byte; uint32_t addr = (fifo->addr_type & FIFO_ADDR); virge->s3d.pattern_8[y * 8 + x] = val & 0xff; virge->s3d.pattern_8[y * 8 + x + 1] = val >> 8; virge->s3d.pattern_8[y * 8 + x + 2] = val >> 16; virge->s3d.pattern_8[y * 8 + x + 3] = val >> 24; x = ((fifo->addr_type & FIFO_ADDR) >> 1) & 6; y = ((fifo->addr_type & FIFO_ADDR) >> 4) & 7; virge->s3d.pattern_16[y * 8 + x] = val & 0xffff; virge->s3d.pattern_16[y * 8 + x + 1] = val >> 16; addr &= 0x00ff; for (uint8_t xx = 0; xx < 4; xx++) { x = ((addr + xx) / 3) % 8; y = ((addr + xx) / 24) % 8; color = ((addr + xx) % 3) << 3; byte = (xx << 3); virge->s3d.pattern_24[y * 8 + x] &= ~(0xff << color); virge->s3d.pattern_24[y * 8 + x] |= ((val >> byte) & 0xff) << color; } x = ((fifo->addr_type & FIFO_ADDR) >> 2) & 7; y = ((fifo->addr_type & FIFO_ADDR) >> 5) & 7; virge->s3d.pattern_32[y * 8 + x] = val & 0xffffff; } break; case 0xa4d4: case 0xa8d4: case 0xacd4: virge->s3d.src_base = val & ((virge->memory_size == 8) ? (val & 0x7ffff8) : (val & 0x3ffff8)); break; case 0xa4d8: case 0xa8d8: case 0xacd8: virge->s3d.dest_base = val & ((virge->memory_size == 8) ? (val & 0x7ffff8) : (val & 0x3ffff8)); break; case 0xa4dc: case 0xa8dc: case 0xacdc: virge->s3d.clip_l = (val >> 16) & 0x7ff; virge->s3d.clip_r = val & 0x7ff; break; case 0xa4e0: case 0xa8e0: case 0xace0: virge->s3d.clip_t = (val >> 16) & 0x7ff; virge->s3d.clip_b = val & 0x7ff; break; case 0xa4e4: case 0xa8e4: case 0xace4: virge->s3d.dest_str = (val >> 16) & 0xff8; virge->s3d.src_str = val & 0xff8; break; case 0xa4e8: case 0xace8: virge->s3d.mono_pat_0 = val; break; case 0xa4ec: case 0xacec: virge->s3d.mono_pat_1 = val; break; case 0xa4f0: case 0xacf0: virge->s3d.pat_bg_clr = val; break; case 0xa4f4: case 0xa8f4: case 0xacf4: virge->s3d.pat_fg_clr = val; break; case 0xa4f8: virge->s3d.src_bg_clr = val; break; case 0xa4fc: virge->s3d.src_fg_clr = val; break; case 0xa500: case 0xa900: case 0xad00: virge->s3d.cmd_set = val; if (!(val & CMD_SET_AE)) s3_virge_bitblt(virge, -1, 0); break; case 0xa504: virge->s3d.r_width = (val >> 16) & 0x7ff; virge->s3d.r_height = val & 0x7ff; break; case 0xa508: virge->s3d.rsrc_x = (val >> 16) & 0x7ff; virge->s3d.rsrc_y = val & 0x7ff; break; case 0xa50c: virge->s3d.rdest_x = (val >> 16) & 0x7ff; virge->s3d.rdest_y = val & 0x7ff; if (virge->s3d.cmd_set & CMD_SET_AE) s3_virge_bitblt(virge, -1, 0); break; case 0xa96c: virge->s3d.lxend0 = (val >> 16) & 0x7ff; virge->s3d.lxend1 = val & 0x7ff; break; case 0xa970: virge->s3d.ldx = (int32_t)val; break; case 0xa974: virge->s3d.lxstart = val; break; case 0xa978: virge->s3d.lystart = val & 0x7ff; break; case 0xa97c: virge->s3d.lycnt = val & 0x7ff; virge->s3d.line_dir = val >> 31; if (virge->s3d.cmd_set & CMD_SET_AE) s3_virge_bitblt(virge, -1, 0); break; case 0xad68: virge->s3d.prdx = val; break; case 0xad6c: virge->s3d.prxstart = val; break; case 0xad70: virge->s3d.pldx = val; break; case 0xad74: virge->s3d.plxstart = val; break; case 0xad78: virge->s3d.pystart = val & 0x7ff; break; case 0xad7c: virge->s3d.pycnt = val & 0x300007ff; if (virge->s3d.cmd_set & CMD_SET_AE) s3_virge_bitblt(virge, -1, 0); break; case 0xb0d4: case 0xb4d4: virge->s3d_tri.z_base = val & ((virge->memory_size == 8) ? (val & 0x7ffff8) : (val & 0x3ffff8)); break; case 0xb0d8: case 0xb4d8: virge->s3d_tri.dest_base = val & ((virge->memory_size == 8) ? (val & 0x7ffff8) : (val & 0x3ffff8)); break; case 0xb0dc: case 0xb4dc: virge->s3d_tri.clip_l = (val >> 16) & 0x7ff; virge->s3d_tri.clip_r = val & 0x7ff; break; case 0xb0e0: case 0xb4e0: virge->s3d_tri.clip_t = (val >> 16) & 0x7ff; virge->s3d_tri.clip_b = val & 0x7ff; break; case 0xb0e4: case 0xb4e4: virge->s3d_tri.dest_str = (val >> 16) & 0xff8; virge->s3d.src_str = val & 0xff8; break; case 0xb0e8: case 0xb4e8: virge->s3d_tri.z_str = val & 0xff8; break; case 0xb4ec: virge->s3d_tri.tex_base = val & ((virge->memory_size == 8) ? (val & 0x7ffff8) : (val & 0x3ffff8)); break; case 0xb4f0: virge->s3d_tri.tex_bdr_clr = val & 0xffffff; break; case 0xb0f4: case 0xb4f4: virge->s3d_tri.fog_b = val & 0xff; virge->s3d_tri.fog_g = (val >> 8) & 0xff; virge->s3d_tri.fog_r = (val >> 16) & 0xff; break; case 0xb100: case 0xb500: virge->s3d_tri.cmd_set = val; if (!(val & CMD_SET_AE)) queue_triangle(virge); break; case 0xb504: virge->s3d_tri.tbv = val & 0xfffff; break; case 0xb508: virge->s3d_tri.tbu = val & 0xfffff; break; case 0xb50c: virge->s3d_tri.TdWdX = val; break; case 0xb510: virge->s3d_tri.TdWdY = val; break; case 0xb514: virge->s3d_tri.tws = val; break; case 0xb518: virge->s3d_tri.TdDdX = val; break; case 0xb51c: virge->s3d_tri.TdVdX = val; break; case 0xb520: virge->s3d_tri.TdUdX = val; break; case 0xb524: virge->s3d_tri.TdDdY = val; break; case 0xb528: virge->s3d_tri.TdVdY = val; break; case 0xb52c: virge->s3d_tri.TdUdY = val; break; case 0xb530: virge->s3d_tri.tds = val; break; case 0xb534: virge->s3d_tri.tvs = val; break; case 0xb538: virge->s3d_tri.tus = val; break; case 0xb53c: virge->s3d_tri.TdGdX = val >> 16; virge->s3d_tri.TdBdX = val & 0xffff; break; case 0xb540: virge->s3d_tri.TdAdX = val >> 16; virge->s3d_tri.TdRdX = val & 0xffff; break; case 0xb544: virge->s3d_tri.TdGdY = val >> 16; virge->s3d_tri.TdBdY = val & 0xffff; break; case 0xb548: virge->s3d_tri.TdAdY = val >> 16; virge->s3d_tri.TdRdY = val & 0xffff; break; case 0xb54c: virge->s3d_tri.tgs = (val >> 16) & 0xffff; virge->s3d_tri.tbs = val & 0xffff; break; case 0xb550: virge->s3d_tri.tas = (val >> 16) & 0xffff; virge->s3d_tri.trs = val & 0xffff; break; case 0xb554: virge->s3d_tri.TdZdX = val; break; case 0xb558: virge->s3d_tri.TdZdY = val; break; case 0xb55c: virge->s3d_tri.tzs = val; break; case 0xb560: virge->s3d_tri.TdXdY12 = val; break; case 0xb564: virge->s3d_tri.txend12 = val; break; case 0xb568: virge->s3d_tri.TdXdY01 = val; break; case 0xb56c: virge->s3d_tri.txend01 = val; break; case 0xb570: virge->s3d_tri.TdXdY02 = val; break; case 0xb574: virge->s3d_tri.txs = val; break; case 0xb578: virge->s3d_tri.tys = val; break; case 0xb57c: virge->s3d_tri.ty01 = (val >> 16) & 0x7ff; virge->s3d_tri.ty12 = val & 0x7ff; virge->s3d_tri.tlr = val >> 31; if (virge->s3d_tri.cmd_set & CMD_SET_AE) queue_triangle(virge); break; } } break; } virge->fifo_read_idx++; fifo->addr_type = FIFO_INVALID; if (FIFO_ENTRIES > 0xe000) thread_set_event(virge->fifo_not_full_event); end_time = plat_timer_read(); virge_time += end_time - start_time; } virge->virge_busy = 0; virge->subsys_stat |= INT_FIFO_EMP | INT_3DF_EMP; s3_virge_update_irqs(virge); } } static void s3_virge_queue(virge_t *virge, uint32_t addr, uint32_t val, uint32_t type) { fifo_entry_t *fifo = &virge->fifo[virge->fifo_write_idx & FIFO_MASK]; int limit = 0; if (type == FIFO_WRITE_DWORD) { switch (addr & 0xfffc) { case 0xa500: if (val & CMD_SET_AE) limit = 1; break; default: break; } } if (limit) { if (FIFO_ENTRIES >= 16) { thread_reset_event(virge->fifo_not_full_event); if (FIFO_ENTRIES >= 16) thread_wait_event(virge->fifo_not_full_event, -1); /*Wait for room in ringbuffer*/ } } else { if (FIFO_FULL) { thread_reset_event(virge->fifo_not_full_event); if (FIFO_FULL) thread_wait_event(virge->fifo_not_full_event, -1); /*Wait for room in ringbuffer*/ } } fifo->val = val; fifo->addr_type = (addr & FIFO_ADDR) | type; virge->fifo_write_idx++; if (FIFO_ENTRIES > 0xe000) wake_fifo_thread(virge); if (FIFO_ENTRIES > 0xe000 || FIFO_ENTRIES < 8) wake_fifo_thread(virge); } static void s3_virge_mmio_write(uint32_t addr, uint8_t val, void *priv) { virge_t *virge = (virge_t *) priv; if ((addr & 0xfffc) < 0x8000) s3_virge_queue(virge, addr, val, FIFO_WRITE_BYTE); else switch (addr & 0xffff) { case 0x83b0 ... 0x83df: s3_virge_out(addr & 0x3ff, val, priv); break; case 0xff20: virge->serialport = val; i2c_gpio_set(virge->i2c, !!(val & SERIAL_PORT_SCW), !!(val & SERIAL_PORT_SDW)); break; } } static void s3_virge_mmio_write_w(uint32_t addr, uint16_t val, void *priv) { virge_t *virge = (virge_t *) priv; if ((addr & 0xfffc) < 0x8000) s3_virge_queue(virge, addr, val, FIFO_WRITE_WORD); else switch (addr & 0xfffe) { case 0x83d4: s3_virge_mmio_write(addr, val, priv); s3_virge_mmio_write(addr + 1, val >> 8, priv); break; case 0xff20: s3_virge_mmio_write(addr, val, priv); break; } } static void s3_virge_mmio_write_l(uint32_t addr, uint32_t val, void *priv) { virge_t *virge = (virge_t *) priv; svga_t * svga = &virge->svga; if ((addr & 0xfffc) < 0x8000) s3_virge_queue(virge, addr, val, FIFO_WRITE_DWORD); else if ((addr & 0xe000) == 0xa000) s3_virge_queue(virge, addr, val, FIFO_WRITE_DWORD); else switch (addr & 0xfffc) { case 0x8180: virge->streams.pri_ctrl = val; svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x8184: virge->streams.chroma_ctrl = val; break; case 0x8190: virge->streams.sec_ctrl = val; virge->streams.dda_horiz_accumulator = val & 0xfff; if (val & (1 << 11)) virge->streams.dda_horiz_accumulator |= 0xfffff800; virge->streams.sdif = (val >> 24) & 7; break; case 0x8194: virge->streams.chroma_upper_bound = val; break; case 0x8198: virge->streams.sec_filter = val; virge->streams.k1_horiz_scale = val & 0x7ff; if (val & (1 << 10)) virge->streams.k1_horiz_scale |= 0xfffff800; virge->streams.k2_horiz_scale = (val >> 16) & 0x7ff; if ((val >> 16) & (1 << 10)) virge->streams.k2_horiz_scale |= 0xfffff800; break; case 0x81a0: virge->streams.blend_ctrl = val; break; case 0x81c0: virge->streams.pri_fb0 = val & ((virge->memory_size == 8) ? (val & 0x7fffff) : (val & 0x3fffff)); svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x81c4: virge->streams.pri_fb1 = ((virge->memory_size == 8) ? (val & 0x7fffff) : (val & 0x3fffff)); svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x81c8: virge->streams.pri_stride = val & 0xfff; svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x81cc: virge->streams.buffer_ctrl = val; svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x81d0: virge->streams.sec_fb0 = val; svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x81d4: virge->streams.sec_fb1 = val; svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x81d8: virge->streams.sec_stride = val; svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x81dc: virge->streams.overlay_ctrl = val; break; case 0x81e0: virge->streams.k1_vert_scale = val & 0x7ff; if (val & (1 << 10)) virge->streams.k1_vert_scale |= 0xfffff800; break; case 0x81e4: virge->streams.k2_vert_scale = val & 0x7ff; if (val & (1 << 10)) virge->streams.k2_vert_scale |= 0xfffff800; break; case 0x81e8: virge->streams.dda_vert_accumulator = val & 0xfff; if (val & (1 << 11)) virge->streams.dda_vert_accumulator |= 0xfffff800; break; case 0x81ec: virge->streams.fifo_ctrl = val; break; case 0x81f0: virge->streams.pri_start = val; virge->streams.pri_x = (val >> 16) & 0x7ff; virge->streams.pri_y = val & 0x7ff; svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x81f4: virge->streams.pri_size = val; virge->streams.pri_w = (val >> 16) & 0x7ff; virge->streams.pri_h = val & 0x7ff; svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x81f8: virge->streams.sec_start = val; virge->streams.sec_x = (val >> 16) & 0x7ff; virge->streams.sec_y = val & 0x7ff; svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x81fc: virge->streams.sec_size = val; virge->streams.sec_w = (val >> 16) & 0x7ff; virge->streams.sec_h = val & 0x7ff; svga_recalctimings(svga); svga->fullchange = changeframecount; break; case 0x8504: virge->subsys_stat &= ~(val & 0xff); virge->subsys_cntl = (val >> 8); s3_virge_update_irqs(virge); break; case 0x850c: virge->advfunc_cntl = val & 0xff; s3_virge_updatemapping(virge); break; case 0xff20: s3_virge_mmio_write(addr, val, priv); break; } } #define READ(addr, val) \ do { \ switch (bpp) { \ case 0: /*8 bpp*/ \ val = vram[addr & svga->vram_mask]; \ break; \ case 1: /*16 bpp*/ \ val = *(uint16_t *)&vram[addr & svga->vram_mask]; \ break; \ case 2: /*24 bpp*/ \ val = (*(uint32_t *)&vram[addr & svga->vram_mask]) & 0xffffff; \ break; \ } \ } while (0) #define Z_READ(addr) *(uint16_t *)&vram[addr & svga->vram_mask] #define Z_WRITE(addr, val) \ if (!(s3d_tri->cmd_set & CMD_SET_ZB_MODE)) \ *(uint16_t *)&vram[addr & svga->vram_mask] = val #define CLIP(x, y) \ do { \ if ((virge->s3d.cmd_set & CMD_SET_HC) && \ (x < virge->s3d.clip_l || x > virge->s3d.clip_r || \ y < virge->s3d.clip_t || y > virge->s3d.clip_b)) \ update = 0; \ } while (0) #define CLIP_3D(x, y) \ do { \ if ((s3d_tri->cmd_set & CMD_SET_HC) && (x < s3d_tri->clip_l || \ x > s3d_tri->clip_r || y < s3d_tri->clip_t || \ y > s3d_tri->clip_b)) \ update = 0; \ } while (0) #define Z_CLIP(Zzb, Zs) \ do { \ if (!(s3d_tri->cmd_set & CMD_SET_ZB_MODE)) \ switch ((s3d_tri->cmd_set >> 20) & 7) { \ case 0: \ update = 0; \ break; \ case 1: \ if (Zs <= Zzb) \ update = 0; \ else \ Zzb = Zs; \ break; \ case 2: \ if (Zs != Zzb) \ update = 0; \ else \ Zzb = Zs; \ break; \ case 3: \ if (Zs < Zzb) \ update = 0; \ else \ Zzb = Zs; \ break; \ case 4: \ if (Zs >= Zzb) \ update = 0; \ else \ Zzb = Zs; \ break; \ case 5: \ if (Zs == Zzb) \ update = 0; \ else \ Zzb = Zs; \ break; \ case 6: \ if (Zs > Zzb) \ update = 0; \ else \ Zzb = Zs; \ break; \ case 7: \ update = 1; \ Zzb = Zs; \ break; \ } \ } while (0) #define MIX() \ do { \ int c; \ for (c = 0; c < 24; c++) { \ int d = (dest & (1 << c)) ? 1 : 0; \ if (source & (1 << c)) \ d |= 2; \ if (pattern & (1 << c)) \ d |= 4; \ if (virge->s3d.rop & (1 << d)) \ out |= (1 << c); \ } \ } while (0) #define WRITE(addr, val) \ do { \ switch (bpp) { \ case 0: /*8 bpp*/ \ vram[addr & svga->vram_mask] = val; \ virge->svga.changedvram[(addr & svga->vram_mask) >> 12] = \ changeframecount; \ break; \ case 1: /*16 bpp*/ \ *(uint16_t *)&vram[addr & svga->vram_mask] = val; \ virge->svga.changedvram[(addr & svga->vram_mask) >> 12] = \ changeframecount; \ break; \ case 2: /*24 bpp*/ \ *(uint32_t *)&vram[addr & svga->vram_mask] = (val & 0xffffff) |\ (vram[(addr + 3) & svga->vram_mask] << 24); \ virge->svga.changedvram[(addr & svga->vram_mask) >> 12] = \ changeframecount; \ break; \ } \ } while (0) static void s3_virge_bitblt(virge_t *virge, int count, uint32_t cpu_dat) { svga_t *svga = &virge->svga; uint8_t *vram = virge->svga.vram; uint32_t mono_pattern[64]; int count_mask; int x_inc = (virge->s3d.cmd_set & CMD_SET_XP) ? 1 : -1; int y_inc = (virge->s3d.cmd_set & CMD_SET_YP) ? 1 : -1; int bpp; int x_mul; int cpu_dat_shift; uint32_t *pattern_data; uint32_t src_fg_clr; uint32_t src_bg_clr; switch (virge->s3d.cmd_set & CMD_SET_FORMAT_MASK) { case CMD_SET_FORMAT_8: bpp = 0; x_mul = 1; cpu_dat_shift = 8; pattern_data = virge->s3d.pattern_8; src_fg_clr = virge->s3d.src_fg_clr & 0xff; src_bg_clr = virge->s3d.src_bg_clr & 0xff; break; case CMD_SET_FORMAT_16: bpp = 1; x_mul = 2; cpu_dat_shift = 16; pattern_data = virge->s3d.pattern_16; src_fg_clr = virge->s3d.src_fg_clr & 0xffff; src_bg_clr = virge->s3d.src_bg_clr & 0xffff; break; case CMD_SET_FORMAT_24: default: bpp = 2; x_mul = 3; cpu_dat_shift = 24; pattern_data = virge->s3d.pattern_24; src_fg_clr = virge->s3d.src_fg_clr; src_bg_clr = virge->s3d.src_bg_clr; break; } if (virge->s3d.cmd_set & CMD_SET_MP) pattern_data = mono_pattern; switch (virge->s3d.cmd_set & CMD_SET_ITA_MASK) { case CMD_SET_ITA_BYTE: count_mask = ~0x7; break; case CMD_SET_ITA_WORD: count_mask = ~0xf; break; case CMD_SET_ITA_DWORD: default: count_mask = ~0x1f; break; } if (virge->s3d.cmd_set & CMD_SET_MP) { int x; int y; for (y = 0; y < 4; y++) { for (x = 0; x < 8; x++) { if (virge->s3d.mono_pat_0 & (1 << (x + y * 8))) mono_pattern[y * 8 + (7 - x)] = virge->s3d.pat_fg_clr; else mono_pattern[y * 8 + (7 - x)] = virge->s3d.pat_bg_clr; if (virge->s3d.mono_pat_1 & (1 << (x + y * 8))) mono_pattern[(y + 4) * 8 + (7 - x)] = virge->s3d.pat_fg_clr; else mono_pattern[(y + 4) * 8 + (7 - x)] = virge->s3d.pat_bg_clr; } } } switch (virge->s3d.cmd_set & CMD_SET_COMMAND_MASK) { case CMD_SET_COMMAND_NOP: break; case CMD_SET_COMMAND_BITBLT: if (count == -1) { virge->s3d.src_x = virge->s3d.rsrc_x; virge->s3d.src_y = virge->s3d.rsrc_y; virge->s3d.dest_x = virge->s3d.rdest_x; virge->s3d.dest_y = virge->s3d.rdest_y; virge->s3d.w = virge->s3d.r_width; virge->s3d.h = virge->s3d.r_height; virge->s3d.rop = (virge->s3d.cmd_set >> 17) & 0xff; virge->s3d.data_left_count = 0; if (virge->s3d.cmd_set & CMD_SET_IDS) return; } if (!virge->s3d.h) return; while (count) { uint32_t src_addr = virge->s3d.src_base + (virge->s3d.src_x * x_mul) + (virge->s3d.src_y * virge->s3d.src_str); uint32_t dest_addr = virge->s3d.dest_base + (virge->s3d.dest_x * x_mul) + (virge->s3d.dest_y * virge->s3d.dest_str); uint32_t source = 0; uint32_t dest; uint32_t pattern; uint32_t out = 0; int update = 1; switch (virge->s3d.cmd_set & (CMD_SET_MS | CMD_SET_IDS)) { case 0: case CMD_SET_MS: READ(src_addr, source); if ((virge->s3d.cmd_set & CMD_SET_TP) && source == src_fg_clr) update = 0; break; case CMD_SET_IDS: if (virge->s3d.data_left_count) { /*Handle shifting for 24-bit data*/ source = virge->s3d.data_left; source |= ((cpu_dat << virge->s3d.data_left_count) & ~0xff000000); cpu_dat >>= (cpu_dat_shift - virge->s3d.data_left_count); count -= (cpu_dat_shift - virge->s3d.data_left_count); virge->s3d.data_left_count = 0; if (count < cpu_dat_shift) { virge->s3d.data_left = cpu_dat; virge->s3d.data_left_count = count; count = 0; } } else { source = cpu_dat; cpu_dat >>= cpu_dat_shift; count -= cpu_dat_shift; if (count < cpu_dat_shift) { virge->s3d.data_left = cpu_dat; virge->s3d.data_left_count = count; count = 0; } } if ((virge->s3d.cmd_set & CMD_SET_TP) && source == src_fg_clr) update = 0; break; case CMD_SET_IDS | CMD_SET_MS: source = (cpu_dat & (1 << 31)) ? src_fg_clr : src_bg_clr; if ((virge->s3d.cmd_set & CMD_SET_TP) && !(cpu_dat & (1 << 31))) update = 0; cpu_dat <<= 1; count--; break; } CLIP(virge->s3d.dest_x, virge->s3d.dest_y); if (update) { READ(dest_addr, dest); pattern = pattern_data[(virge->s3d.dest_y & 7) * 8 + (virge->s3d.dest_x & 7)]; MIX(); WRITE(dest_addr, out); } virge->s3d.src_x += x_inc; virge->s3d.src_x &= 0x7ff; virge->s3d.dest_x += x_inc; virge->s3d.dest_x &= 0x7ff; if (!virge->s3d.w) { virge->s3d.src_x = virge->s3d.rsrc_x; virge->s3d.dest_x = virge->s3d.rdest_x; virge->s3d.w = virge->s3d.r_width; virge->s3d.src_y += y_inc; virge->s3d.dest_y += y_inc; virge->s3d.h--; switch (virge->s3d.cmd_set & (CMD_SET_MS | CMD_SET_IDS)) { case CMD_SET_IDS: cpu_dat >>= (count - (count & count_mask)); count &= count_mask; virge->s3d.data_left_count = 0; break; case CMD_SET_IDS | CMD_SET_MS: cpu_dat <<= (count - (count & count_mask)); count &= count_mask; break; } if (!virge->s3d.h) return; } else virge->s3d.w--; } break; case CMD_SET_COMMAND_RECTFILL: /*No source, pattern = pat_fg_clr*/ if (count == -1) { virge->s3d.src_x = virge->s3d.rsrc_x; virge->s3d.src_y = virge->s3d.rsrc_y; virge->s3d.dest_x = virge->s3d.rdest_x; virge->s3d.dest_y = virge->s3d.rdest_y; virge->s3d.w = virge->s3d.r_width; virge->s3d.h = virge->s3d.r_height; virge->s3d.rop = (virge->s3d.cmd_set >> 17) & 0xff; } while (count && virge->s3d.h) { uint32_t dest_addr = virge->s3d.dest_base + (virge->s3d.dest_x * x_mul) + (virge->s3d.dest_y * virge->s3d.dest_str); uint32_t source = 0; uint32_t dest; uint32_t pattern = virge->s3d.pat_fg_clr; uint32_t out = 0; int update = 1; CLIP(virge->s3d.dest_x, virge->s3d.dest_y); if (update) { READ(dest_addr, dest); MIX(); WRITE(dest_addr, out); } virge->s3d.src_x += x_inc; virge->s3d.src_x &= 0x7ff; virge->s3d.dest_x += x_inc; virge->s3d.dest_x &= 0x7ff; if (!virge->s3d.w) { virge->s3d.src_x = virge->s3d.rsrc_x; virge->s3d.dest_x = virge->s3d.rdest_x; virge->s3d.w = virge->s3d.r_width; virge->s3d.src_y += y_inc; virge->s3d.dest_y += y_inc; virge->s3d.h--; if (!virge->s3d.h) return; } else virge->s3d.w--; count--; } break; case CMD_SET_COMMAND_LINE: if (count == -1) { virge->s3d.dest_x = virge->s3d.lxstart; virge->s3d.dest_y = virge->s3d.lystart; virge->s3d.h = virge->s3d.lycnt; virge->s3d.rop = (virge->s3d.cmd_set >> 17) & 0xff; } while (virge->s3d.h) { int x; int new_x; int first_pixel = 1; x = virge->s3d.dest_x >> 20; if (virge->s3d.h == virge->s3d.lycnt && ((virge->s3d.line_dir && x > virge->s3d.lxend0) || (!virge->s3d.line_dir && x < virge->s3d.lxend0))) x = virge->s3d.lxend0; if (virge->s3d.h == 1) new_x = virge->s3d.lxend1 + (virge->s3d.line_dir ? 1 : -1); else new_x = (virge->s3d.dest_x + virge->s3d.ldx) >> 20; if ((virge->s3d.line_dir && x > new_x) || (!virge->s3d.line_dir && x < new_x)) goto skip_line; do { uint32_t dest_addr = virge->s3d.dest_base + (x * x_mul) + (virge->s3d.dest_y * virge->s3d.dest_str); uint32_t source = 0; uint32_t dest; uint32_t pattern; uint32_t out = 0; int update = 1; if ((virge->s3d.h == virge->s3d.lycnt || !first_pixel) && ((virge->s3d.line_dir && x < virge->s3d.lxend0) || (!virge->s3d.line_dir && x > virge->s3d.lxend0))) update = 0; if ((virge->s3d.h == 1 || !first_pixel) && ((virge->s3d.line_dir && x > virge->s3d.lxend1) || (!virge->s3d.line_dir && x < virge->s3d.lxend1))) update = 0; CLIP(x, virge->s3d.dest_y); if (update) { READ(dest_addr, dest); pattern = virge->s3d.pat_fg_clr; MIX(); WRITE(dest_addr, out); } if (x < new_x) x++; else if (x > new_x) x--; first_pixel = 0; } while (x != new_x); skip_line: virge->s3d.dest_x += virge->s3d.ldx; virge->s3d.dest_y--; virge->s3d.h--; } break; case CMD_SET_COMMAND_POLY: /*No source*/ if (virge->s3d.pycnt & (1 << 28)) virge->s3d.dest_r = virge->s3d.prxstart; if (virge->s3d.pycnt & (1 << 29)) virge->s3d.dest_l = virge->s3d.plxstart; virge->s3d.h = virge->s3d.pycnt & 0x7ff; virge->s3d.rop = (virge->s3d.cmd_set >> 17) & 0xff; while (virge->s3d.h) { int x = virge->s3d.dest_l >> 20; int xend = virge->s3d.dest_r >> 20; int y = virge->s3d.pystart & 0x7ff; int xdir = (x < xend) ? 1 : -1; do { uint32_t dest_addr = virge->s3d.dest_base + (x * x_mul) + (y * virge->s3d.dest_str); uint32_t source = 0; uint32_t dest; uint32_t pattern; uint32_t out = 0; int update = 1; CLIP(x, y); if (update) { READ(dest_addr, dest); pattern = pattern_data[(y & 7) * 8 + (x & 7)]; MIX(); WRITE(dest_addr, out); } x = (x + xdir) & 0x7ff; } while (x != (xend + xdir)); virge->s3d.dest_l += virge->s3d.pldx; virge->s3d.dest_r += virge->s3d.prdx; virge->s3d.h--; virge->s3d.pystart = (virge->s3d.pystart - 1) & 0x7ff; } break; default: fatal("s3_virge_bitblt : blit command %i %08x\n", (virge->s3d.cmd_set >> 27) & 0xf, virge->s3d.cmd_set); } } #define RGB15_TO_24(val, r, g, b) \ b = ((val & 0x001f) << 3) | ((val & 0x001f) >> 2); \ g = ((val & 0x03e0) >> 2) | ((val & 0x03e0) >> 7); \ r = ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12); #define RGB24_TO_24(val, r, g, b) \ b = val & 0xff; \ g = (val & 0xff00) >> 8; \ r = (val & 0xff0000) >> 16 #define RGB15(r, g, b, dest) \ if (virge->dithering_enabled) { \ int add = dither[_y & 3][_x & 3]; \ int _r = (r > 248) ? 248 : r + add; \ int _g = (g > 248) ? 248 : g + add; \ int _b = (b > 248) ? 248 : b + add; \ dest = ((_b >> 3) & 0x1f) | \ (((_g >> 3) & 0x1f) << 5) | \ (((_r >> 3) & 0x1f) << 10); \ } else \ dest = ((b >> 3) & 0x1f) | \ (((g >> 3) & 0x1f) << 5) | \ (((r >> 3) & 0x1f) << 10) #define RGB24(r, g, b) ((b) | ((g) << 8) | ((r) << 16)) typedef struct rgba_t { int r, g, b, a; } rgba_t; typedef struct s3d_state_t { int32_t r; int32_t g; int32_t b; int32_t a; int32_t u; int32_t v; int32_t d; int32_t w; int32_t base_r; int32_t base_g; int32_t base_b; int32_t base_a; int32_t base_u; int32_t base_v; int32_t base_d; int32_t base_w; uint32_t base_z; uint32_t tbu; uint32_t tbv; uint32_t cmd_set; int max_d; uint16_t *texture[10]; uint32_t tex_bdr_clr; int32_t x1; int32_t x2; int y; rgba_t dest_rgba; } s3d_state_t; typedef struct s3d_texture_state_t { int level; int texture_shift; int32_t u; int32_t v; } s3d_texture_state_t; static void (*tex_read)(s3d_state_t *state, s3d_texture_state_t *texture_state, rgba_t *out); static void (*tex_sample)(s3d_state_t *state); static void (*dest_pixel)(s3d_state_t *state); #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) static int _x; static int _y; static void tex_ARGB1555(s3d_state_t *state, s3d_texture_state_t *texture_state, rgba_t *out) { int offset = ((texture_state->u & 0x7fc0000) >> texture_state->texture_shift) + (((texture_state->v & 0x7fc0000) >> texture_state->texture_shift) << texture_state->level); uint16_t val = state->texture[texture_state->level][offset]; out->r = ((val & 0x7c00) >> 7) | ((val & 0x7000) >> 12); out->g = ((val & 0x03e0) >> 2) | ((val & 0x0380) >> 7); out->b = ((val & 0x001f) << 3) | ((val & 0x001c) >> 2); out->a = (val & 0x8000) ? 0xff : 0; } static void tex_ARGB1555_nowrap(s3d_state_t *state, s3d_texture_state_t *texture_state, rgba_t *out) { int offset = ((texture_state->u & 0x7fc0000) >> texture_state->texture_shift) + (((texture_state->v & 0x7fc0000) >> texture_state->texture_shift) << texture_state->level); uint16_t val = state->texture[texture_state->level][offset]; if (((texture_state->u | texture_state->v) & 0xf8000000) == 0xf8000000) val = state->tex_bdr_clr; out->r = ((val & 0x7c00) >> 7) | ((val & 0x7000) >> 12); out->g = ((val & 0x03e0) >> 2) | ((val & 0x0380) >> 7); out->b = ((val & 0x001f) << 3) | ((val & 0x001c) >> 2); out->a = (val & 0x8000) ? 0xff : 0; } static void tex_ARGB4444(s3d_state_t *state, s3d_texture_state_t *texture_state, rgba_t *out) { int offset = ((texture_state->u & 0x7fc0000) >> texture_state->texture_shift) + (((texture_state->v & 0x7fc0000) >> texture_state->texture_shift) << texture_state->level); uint16_t val = state->texture[texture_state->level][offset]; out->r = ((val & 0x0f00) >> 4) | ((val & 0x0f00) >> 8); out->g = (val & 0x00f0) | ((val & 0x00f0) >> 4); out->b = ((val & 0x000f) << 4) | (val & 0x000f); out->a = ((val & 0xf000) >> 8) | ((val & 0xf000) >> 12); } static void tex_ARGB4444_nowrap(s3d_state_t *state, s3d_texture_state_t *texture_state, rgba_t *out) { int offset = ((texture_state->u & 0x7fc0000) >> texture_state->texture_shift) + (((texture_state->v & 0x7fc0000) >> texture_state->texture_shift) << texture_state->level); uint16_t val = state->texture[texture_state->level][offset]; if (((texture_state->u | texture_state->v) & 0xf8000000) == 0xf8000000) val = state->tex_bdr_clr; out->r = ((val & 0x0f00) >> 4) | ((val & 0x0f00) >> 8); out->g = (val & 0x00f0) | ((val & 0x00f0) >> 4); out->b = ((val & 0x000f) << 4) | (val & 0x000f); out->a = ((val & 0xf000) >> 8) | ((val & 0xf000) >> 12); } static void tex_ARGB8888(s3d_state_t *state, s3d_texture_state_t *texture_state, rgba_t *out) { int offset = ((texture_state->u & 0x7fc0000) >> texture_state->texture_shift) + (((texture_state->v & 0x7fc0000) >> texture_state->texture_shift) << texture_state->level); uint32_t val = ((uint32_t *)state->texture[texture_state->level])[offset]; out->r = (val >> 16) & 0xff; out->g = (val >> 8) & 0xff; out->b = val & 0xff; out->a = (val >> 24) & 0xff; } static void tex_ARGB8888_nowrap(s3d_state_t *state, s3d_texture_state_t *texture_state, rgba_t *out) { int offset = ((texture_state->u & 0x7fc0000) >> texture_state->texture_shift) + (((texture_state->v & 0x7fc0000) >> texture_state->texture_shift) << texture_state->level); uint32_t val = ((uint32_t *)state->texture[texture_state->level])[offset]; if (((texture_state->u | texture_state->v) & 0xf8000000) == 0xf8000000) val = state->tex_bdr_clr; out->r = (val >> 16) & 0xff; out->g = (val >> 8) & 0xff; out->b = val & 0xff; out->a = (val >> 24) & 0xff; } static void tex_sample_normal(s3d_state_t *state) { s3d_texture_state_t texture_state; texture_state.level = state->max_d; texture_state.texture_shift = 18 + (9 - texture_state.level); texture_state.u = state->u + state->tbu; texture_state.v = state->v + state->tbv; tex_read(state, &texture_state, &state->dest_rgba); } static void tex_sample_normal_filter(s3d_state_t *state) { s3d_texture_state_t texture_state; int tex_offset; rgba_t tex_samples[4]; int du; int dv; int d[4]; texture_state.level = state->max_d; texture_state.texture_shift = 18 + (9 - texture_state.level); tex_offset = 1 << texture_state.texture_shift; texture_state.u = state->u + state->tbu; texture_state.v = state->v + state->tbv; tex_read(state, &texture_state, &tex_samples[0]); du = (texture_state.u >> (texture_state.texture_shift - 8)) & 0xff; dv = (texture_state.v >> (texture_state.texture_shift - 8)) & 0xff; texture_state.u = state->u + state->tbu + tex_offset; texture_state.v = state->v + state->tbv; tex_read(state, &texture_state, &tex_samples[1]); texture_state.u = state->u + state->tbu; texture_state.v = state->v + state->tbv + tex_offset; tex_read(state, &texture_state, &tex_samples[2]); texture_state.u = state->u + state->tbu + tex_offset; texture_state.v = state->v + state->tbv + tex_offset; tex_read(state, &texture_state, &tex_samples[3]); d[0] = (256 - du) * (256 - dv); d[1] = du * (256 - dv); d[2] = (256 - du) * dv; d[3] = du * dv; state->dest_rgba.r = (tex_samples[0].r * d[0] + tex_samples[1].r * d[1] + tex_samples[2].r * d[2] + tex_samples[3].r * d[3]) >> 16; state->dest_rgba.g = (tex_samples[0].g * d[0] + tex_samples[1].g * d[1] + tex_samples[2].g * d[2] + tex_samples[3].g * d[3]) >> 16; state->dest_rgba.b = (tex_samples[0].b * d[0] + tex_samples[1].b * d[1] + tex_samples[2].b * d[2] + tex_samples[3].b * d[3]) >> 16; state->dest_rgba.a = (tex_samples[0].a * d[0] + tex_samples[1].a * d[1] + tex_samples[2].a * d[2] + tex_samples[3].a * d[3]) >> 16; } static void tex_sample_mipmap(s3d_state_t *state) { s3d_texture_state_t texture_state; texture_state.level = (state->d < 0) ? state->max_d : state->max_d - ((state->d >> 27) & 0xf); if (texture_state.level < 0) texture_state.level = 0; texture_state.texture_shift = 18 + (9 - texture_state.level); texture_state.u = state->u + state->tbu; texture_state.v = state->v + state->tbv; tex_read(state, &texture_state, &state->dest_rgba); } static void tex_sample_mipmap_filter(s3d_state_t *state) { s3d_texture_state_t texture_state; int tex_offset; rgba_t tex_samples[4]; int du; int dv; int d[4]; texture_state.level = (state->d < 0) ? state->max_d : state->max_d - ((state->d >> 27) & 0xf); if (texture_state.level < 0) texture_state.level = 0; texture_state.texture_shift = 18 + (9 - texture_state.level); tex_offset = 1 << texture_state.texture_shift; texture_state.u = state->u + state->tbu; texture_state.v = state->v + state->tbv; tex_read(state, &texture_state, &tex_samples[0]); du = (texture_state.u >> (texture_state.texture_shift - 8)) & 0xff; dv = (texture_state.v >> (texture_state.texture_shift - 8)) & 0xff; texture_state.u = state->u + state->tbu + tex_offset; texture_state.v = state->v + state->tbv; tex_read(state, &texture_state, &tex_samples[1]); texture_state.u = state->u + state->tbu; texture_state.v = state->v + state->tbv + tex_offset; tex_read(state, &texture_state, &tex_samples[2]); texture_state.u = state->u + state->tbu + tex_offset; texture_state.v = state->v + state->tbv + tex_offset; tex_read(state, &texture_state, &tex_samples[3]); d[0] = (256 - du) * (256 - dv); d[1] = du * (256 - dv); d[2] = (256 - du) * dv; d[3] = du * dv; state->dest_rgba.r = (tex_samples[0].r * d[0] + tex_samples[1].r * d[1] + tex_samples[2].r * d[2] + tex_samples[3].r * d[3]) >> 16; state->dest_rgba.g = (tex_samples[0].g * d[0] + tex_samples[1].g * d[1] + tex_samples[2].g * d[2] + tex_samples[3].g * d[3]) >> 16; state->dest_rgba.b = (tex_samples[0].b * d[0] + tex_samples[1].b * d[1] + tex_samples[2].b * d[2] + tex_samples[3].b * d[3]) >> 16; state->dest_rgba.a = (tex_samples[0].a * d[0] + tex_samples[1].a * d[1] + tex_samples[2].a * d[2] + tex_samples[3].a * d[3]) >> 16; } static void tex_sample_persp_normal(s3d_state_t *state) { s3d_texture_state_t texture_state; int32_t w = 0; if (state->w) w = (int32_t)(((1ULL << 27) << 19) / (int64_t)state->w); texture_state.level = state->max_d; texture_state.texture_shift = 18 + (9 - texture_state.level); texture_state.u = (int32_t)(((int64_t)state->u * (int64_t)w) >> (12 + state->max_d)) + state->tbu; texture_state.v = (int32_t)(((int64_t)state->v * (int64_t)w) >> (12 + state->max_d)) + state->tbv; tex_read(state, &texture_state, &state->dest_rgba); } static void tex_sample_persp_normal_filter(s3d_state_t *state) { s3d_texture_state_t texture_state; int32_t w = 0; int32_t u; int32_t v; int tex_offset; rgba_t tex_samples[4]; int du; int dv; int d[4]; if (state->w) w = (int32_t)(((1ULL << 27) << 19) / (int64_t)state->w); u = (int32_t)(((int64_t)state->u * (int64_t)w) >> (12 + state->max_d)) + state->tbu; v = (int32_t)(((int64_t)state->v * (int64_t)w) >> (12 + state->max_d)) + state->tbv; texture_state.level = state->max_d; texture_state.texture_shift = 18 + (9 - texture_state.level); tex_offset = 1 << texture_state.texture_shift; texture_state.u = u; texture_state.v = v; tex_read(state, &texture_state, &tex_samples[0]); du = (u >> (texture_state.texture_shift - 8)) & 0xff; dv = (v >> (texture_state.texture_shift - 8)) & 0xff; texture_state.u = u + tex_offset; texture_state.v = v; tex_read(state, &texture_state, &tex_samples[1]); texture_state.u = u; texture_state.v = v + tex_offset; tex_read(state, &texture_state, &tex_samples[2]); texture_state.u = u + tex_offset; texture_state.v = v + tex_offset; tex_read(state, &texture_state, &tex_samples[3]); d[0] = (256 - du) * (256 - dv); d[1] = du * (256 - dv); d[2] = (256 - du) * dv; d[3] = du * dv; state->dest_rgba.r = (tex_samples[0].r * d[0] + tex_samples[1].r * d[1] + tex_samples[2].r * d[2] + tex_samples[3].r * d[3]) >> 16; state->dest_rgba.g = (tex_samples[0].g * d[0] + tex_samples[1].g * d[1] + tex_samples[2].g * d[2] + tex_samples[3].g * d[3]) >> 16; state->dest_rgba.b = (tex_samples[0].b * d[0] + tex_samples[1].b * d[1] + tex_samples[2].b * d[2] + tex_samples[3].b * d[3]) >> 16; state->dest_rgba.a = (tex_samples[0].a * d[0] + tex_samples[1].a * d[1] + tex_samples[2].a * d[2] + tex_samples[3].a * d[3]) >> 16; } static void tex_sample_persp_normal_375(s3d_state_t *state) { s3d_texture_state_t texture_state; int32_t w = 0; if (state->w) w = (int32_t)(((1ULL << 27) << 19) / (int64_t)state->w); texture_state.level = state->max_d; texture_state.texture_shift = 18 + (9 - texture_state.level); texture_state.u = (int32_t)(((int64_t)state->u * (int64_t)w) >> (8 + state->max_d)) + state->tbu; texture_state.v = (int32_t)(((int64_t)state->v * (int64_t)w) >> (8 + state->max_d)) + state->tbv; tex_read(state, &texture_state, &state->dest_rgba); } static void tex_sample_persp_normal_filter_375(s3d_state_t *state) { s3d_texture_state_t texture_state; int32_t w = 0; int32_t u; int32_t v; int tex_offset; rgba_t tex_samples[4]; int du; int dv; int d[4]; if (state->w) w = (int32_t)(((1ULL << 27) << 19) / (int64_t)state->w); u = (int32_t)(((int64_t)state->u * (int64_t)w) >> (8 + state->max_d)) + state->tbu; v = (int32_t)(((int64_t)state->v * (int64_t)w) >> (8 + state->max_d)) + state->tbv; texture_state.level = state->max_d; texture_state.texture_shift = 18 + (9 - texture_state.level); tex_offset = 1 << texture_state.texture_shift; texture_state.u = u; texture_state.v = v; tex_read(state, &texture_state, &tex_samples[0]); du = (u >> (texture_state.texture_shift - 8)) & 0xff; dv = (v >> (texture_state.texture_shift - 8)) & 0xff; texture_state.u = u + tex_offset; texture_state.v = v; tex_read(state, &texture_state, &tex_samples[1]); texture_state.u = u; texture_state.v = v + tex_offset; tex_read(state, &texture_state, &tex_samples[2]); texture_state.u = u + tex_offset; texture_state.v = v + tex_offset; tex_read(state, &texture_state, &tex_samples[3]); d[0] = (256 - du) * (256 - dv); d[1] = du * (256 - dv); d[2] = (256 - du) * dv; d[3] = du * dv; state->dest_rgba.r = (tex_samples[0].r * d[0] + tex_samples[1].r * d[1] + tex_samples[2].r * d[2] + tex_samples[3].r * d[3]) >> 16; state->dest_rgba.g = (tex_samples[0].g * d[0] + tex_samples[1].g * d[1] + tex_samples[2].g * d[2] + tex_samples[3].g * d[3]) >> 16; state->dest_rgba.b = (tex_samples[0].b * d[0] + tex_samples[1].b * d[1] + tex_samples[2].b * d[2] + tex_samples[3].b * d[3]) >> 16; state->dest_rgba.a = (tex_samples[0].a * d[0] + tex_samples[1].a * d[1] + tex_samples[2].a * d[2] + tex_samples[3].a * d[3]) >> 16; } static void tex_sample_persp_mipmap(s3d_state_t *state) { s3d_texture_state_t texture_state; int32_t w = 0; if (state->w) w = (int32_t)(((1ULL << 27) << 19) / (int64_t)state->w); texture_state.level = (state->d < 0) ? state->max_d : state->max_d - ((state->d >> 27) & 0xf); if (texture_state.level < 0) texture_state.level = 0; texture_state.texture_shift = 18 + (9 - texture_state.level); texture_state.u = (int32_t)(((int64_t)state->u * (int64_t)w) >> (12 + state->max_d)) + state->tbu; texture_state.v = (int32_t)(((int64_t)state->v * (int64_t)w) >> (12 + state->max_d)) + state->tbv; tex_read(state, &texture_state, &state->dest_rgba); } static void tex_sample_persp_mipmap_filter(s3d_state_t *state) { s3d_texture_state_t texture_state; int32_t w = 0; int32_t u; int32_t v; int tex_offset; rgba_t tex_samples[4]; int du; int dv; int d[4]; if (state->w) w = (int32_t)(((1ULL << 27) << 19) / (int64_t)state->w); u = (int32_t)(((int64_t)state->u * (int64_t)w) >> (12 + state->max_d)) + state->tbu; v = (int32_t)(((int64_t)state->v * (int64_t)w) >> (12 + state->max_d)) + state->tbv; texture_state.level = (state->d < 0) ? state->max_d : state->max_d - ((state->d >> 27) & 0xf); if (texture_state.level < 0) texture_state.level = 0; texture_state.texture_shift = 18 + (9 - texture_state.level); tex_offset = 1 << texture_state.texture_shift; texture_state.u = u; texture_state.v = v; tex_read(state, &texture_state, &tex_samples[0]); du = (u >> (texture_state.texture_shift - 8)) & 0xff; dv = (v >> (texture_state.texture_shift - 8)) & 0xff; texture_state.u = u + tex_offset; texture_state.v = v; tex_read(state, &texture_state, &tex_samples[1]); texture_state.u = u; texture_state.v = v + tex_offset; tex_read(state, &texture_state, &tex_samples[2]); texture_state.u = u + tex_offset; texture_state.v = v + tex_offset; tex_read(state, &texture_state, &tex_samples[3]); d[0] = (256 - du) * (256 - dv); d[1] = du * (256 - dv); d[2] = (256 - du) * dv; d[3] = du * dv; state->dest_rgba.r = (tex_samples[0].r * d[0] + tex_samples[1].r * d[1] + tex_samples[2].r * d[2] + tex_samples[3].r * d[3]) >> 16; state->dest_rgba.g = (tex_samples[0].g * d[0] + tex_samples[1].g * d[1] + tex_samples[2].g * d[2] + tex_samples[3].g * d[3]) >> 16; state->dest_rgba.b = (tex_samples[0].b * d[0] + tex_samples[1].b * d[1] + tex_samples[2].b * d[2] + tex_samples[3].b * d[3]) >> 16; state->dest_rgba.a = (tex_samples[0].a * d[0] + tex_samples[1].a * d[1] + tex_samples[2].a * d[2] + tex_samples[3].a * d[3]) >> 16; } static void tex_sample_persp_mipmap_375(s3d_state_t *state) { s3d_texture_state_t texture_state; int32_t w = 0; if (state->w) w = (int32_t)(((1ULL << 27) << 19) / (int64_t)state->w); texture_state.level = (state->d < 0) ? state->max_d : state->max_d - ((state->d >> 27) & 0xf); if (texture_state.level < 0) texture_state.level = 0; texture_state.texture_shift = 18 + (9 - texture_state.level); texture_state.u = (int32_t)(((int64_t)state->u * (int64_t)w) >> (8 + state->max_d)) + state->tbu; texture_state.v = (int32_t)(((int64_t)state->v * (int64_t)w) >> (8 + state->max_d)) + state->tbv; tex_read(state, &texture_state, &state->dest_rgba); } static void tex_sample_persp_mipmap_filter_375(s3d_state_t *state) { s3d_texture_state_t texture_state; int32_t w = 0; int32_t u; int32_t v; int tex_offset; rgba_t tex_samples[4]; int du; int dv; int d[4]; if (state->w) w = (int32_t)(((1ULL << 27) << 19) / (int64_t)state->w); u = (int32_t)(((int64_t)state->u * (int64_t)w) >> (8 + state->max_d)) + state->tbu; v = (int32_t)(((int64_t)state->v * (int64_t)w) >> (8 + state->max_d)) + state->tbv; texture_state.level = (state->d < 0) ? state->max_d : state->max_d - ((state->d >> 27) & 0xf); if (texture_state.level < 0) texture_state.level = 0; texture_state.texture_shift = 18 + (9 - texture_state.level); tex_offset = 1 << texture_state.texture_shift; texture_state.u = u; texture_state.v = v; tex_read(state, &texture_state, &tex_samples[0]); du = (u >> (texture_state.texture_shift - 8)) & 0xff; dv = (v >> (texture_state.texture_shift - 8)) & 0xff; texture_state.u = u + tex_offset; texture_state.v = v; tex_read(state, &texture_state, &tex_samples[1]); texture_state.u = u; texture_state.v = v + tex_offset; tex_read(state, &texture_state, &tex_samples[2]); texture_state.u = u + tex_offset; texture_state.v = v + tex_offset; tex_read(state, &texture_state, &tex_samples[3]); d[0] = (256 - du) * (256 - dv); d[1] = du * (256 - dv); d[2] = (256 - du) * dv; d[3] = du * dv; state->dest_rgba.r = (tex_samples[0].r * d[0] + tex_samples[1].r * d[1] + tex_samples[2].r * d[2] + tex_samples[3].r * d[3]) >> 16; state->dest_rgba.g = (tex_samples[0].g * d[0] + tex_samples[1].g * d[1] + tex_samples[2].g * d[2] + tex_samples[3].g * d[3]) >> 16; state->dest_rgba.b = (tex_samples[0].b * d[0] + tex_samples[1].b * d[1] + tex_samples[2].b * d[2] + tex_samples[3].b * d[3]) >> 16; state->dest_rgba.a = (tex_samples[0].a * d[0] + tex_samples[1].a * d[1] + tex_samples[2].a * d[2] + tex_samples[3].a * d[3]) >> 16; } #define CLAMP(x) \ do { \ if ((x) & ~0xff) \ x = ((x) < 0) ? 0 : 0xff; \ } while (0) #define CLAMP_RGBA(r, g, b, a) \ if ((r) & ~0xff) \ r = ((r) < 0) ? 0 : 0xff; \ if ((g) & ~0xff) \ g = ((g) < 0) ? 0 : 0xff; \ if ((b) & ~0xff) \ b = ((b) < 0) ? 0 : 0xff; \ if ((a) & ~0xff) \ a = ((a) < 0) ? 0 : 0xff; #define CLAMP_RGB(r, g, b) \ do { \ if ((r) < 0) \ r = 0; \ if ((r) > 0xff) \ r = 0xff; \ if ((g) < 0) \ g = 0; \ if ((g) > 0xff) \ g = 0xff; \ if ((b) < 0) \ b = 0; \ if ((b) > 0xff) \ b = 0xff; \ } while (0) static void dest_pixel_gouraud_shaded_triangle(s3d_state_t *state) { state->dest_rgba.r = state->r >> 7; CLAMP(state->dest_rgba.r); state->dest_rgba.g = state->g >> 7; CLAMP(state->dest_rgba.g); state->dest_rgba.b = state->b >> 7; CLAMP(state->dest_rgba.b); state->dest_rgba.a = state->a >> 7; CLAMP(state->dest_rgba.a); } static void dest_pixel_unlit_texture_triangle(s3d_state_t *state) { tex_sample(state); if (state->cmd_set & CMD_SET_ABC_SRC) state->dest_rgba.a = state->a >> 7; } static void dest_pixel_lit_texture_decal(s3d_state_t *state) { tex_sample(state); if (state->cmd_set & CMD_SET_ABC_SRC) state->dest_rgba.a = state->a >> 7; } static void dest_pixel_lit_texture_reflection(s3d_state_t *state) { tex_sample(state); state->dest_rgba.r += (state->r >> 7); state->dest_rgba.g += (state->g >> 7); state->dest_rgba.b += (state->b >> 7); if (state->cmd_set & CMD_SET_ABC_SRC) state->dest_rgba.a += (state->a >> 7); CLAMP_RGBA(state->dest_rgba.r, state->dest_rgba.g, state->dest_rgba.b, state->dest_rgba.a); } static void dest_pixel_lit_texture_modulate(s3d_state_t *state) { int r = state->r >> 7; int g = state->g >> 7; int b = state->b >> 7; int a = state->a >> 7; tex_sample(state); CLAMP_RGBA(r, g, b, a); state->dest_rgba.r = ((state->dest_rgba.r) * r) >> 8; state->dest_rgba.g = ((state->dest_rgba.g) * g) >> 8; state->dest_rgba.b = ((state->dest_rgba.b) * b) >> 8; if (state->cmd_set & CMD_SET_ABC_SRC) state->dest_rgba.a = a; } static void tri(virge_t *virge, s3d_t *s3d_tri, s3d_state_t *state, int yc, int32_t dx1, int32_t dx2) { svga_t *svga = &virge->svga; uint8_t *vram = virge->svga.vram; int x_dir = s3d_tri->tlr ? 1 : -1; int use_z = !(s3d_tri->cmd_set & CMD_SET_ZB_MODE); int y_count = yc; int bpp = (s3d_tri->cmd_set >> 2) & 7; uint32_t dest_offset; uint32_t z_offset; dest_offset = s3d_tri->dest_base + (state->y * s3d_tri->dest_str); z_offset = s3d_tri->z_base + (state->y * s3d_tri->z_str); if (s3d_tri->cmd_set & CMD_SET_HC) { if (state->y < s3d_tri->clip_t) return; if (state->y > s3d_tri->clip_b) { int diff_y = state->y - s3d_tri->clip_b; if (diff_y > y_count) diff_y = y_count; state->base_u += (s3d_tri->TdUdY * diff_y); state->base_v += (s3d_tri->TdVdY * diff_y); state->base_z += (s3d_tri->TdZdY * diff_y); state->base_r += (s3d_tri->TdRdY * diff_y); state->base_g += (s3d_tri->TdGdY * diff_y); state->base_b += (s3d_tri->TdBdY * diff_y); state->base_a += (s3d_tri->TdAdY * diff_y); state->base_d += (s3d_tri->TdDdY * diff_y); state->base_w += (s3d_tri->TdWdY * diff_y); state->x1 += (dx1 * diff_y); state->x2 += (dx2 * diff_y); state->y -= diff_y; dest_offset -= s3d_tri->dest_str; z_offset -= s3d_tri->z_str; y_count -= diff_y; } if ((state->y - y_count) < s3d_tri->clip_t) y_count = (state->y - s3d_tri->clip_t) + 1; } for (; y_count > 0; y_count--) { int x = (state->x1 + ((1 << 20) - 1)) >> 20; int xe = (state->x2 + ((1 << 20) - 1)) >> 20; uint32_t z = (state->base_z > 0) ? (state->base_z << 1) : 0; if (x_dir < 0) { x--; xe--; } if (x != xe && ((x_dir > 0 && x < xe) || (x_dir < 0 && x > xe))) { uint32_t dest_addr; uint32_t z_addr; int dx = (x_dir > 0) ? ((31 - ((state->x1 - 1) >> 15)) & 0x1f) : (((state->x1 - 1) >> 15) & 0x1f); int x_offset = x_dir * (bpp + 1); int xz_offset = x_dir << 1; if (x_dir > 0) dx += 1; state->r = state->base_r + ((s3d_tri->TdRdX * dx) >> 5); state->g = state->base_g + ((s3d_tri->TdGdX * dx) >> 5); state->b = state->base_b + ((s3d_tri->TdBdX * dx) >> 5); state->a = state->base_a + ((s3d_tri->TdAdX * dx) >> 5); state->u = state->base_u + ((s3d_tri->TdUdX * dx) >> 5); state->v = state->base_v + ((s3d_tri->TdVdX * dx) >> 5); state->w = state->base_w + ((s3d_tri->TdWdX * dx) >> 5); state->d = state->base_d + ((s3d_tri->TdDdX * dx) >> 5); z += ((s3d_tri->TdZdX * dx) >> 5); if (s3d_tri->cmd_set & CMD_SET_HC) { if (x_dir > 0) { if (x > s3d_tri->clip_r) goto tri_skip_line; if (xe < s3d_tri->clip_l) goto tri_skip_line; if (xe > s3d_tri->clip_r) xe = s3d_tri->clip_r + 1; if (x < s3d_tri->clip_l) { int diff_x = s3d_tri->clip_l - x; z += (s3d_tri->TdZdX * diff_x); state->u += (s3d_tri->TdUdX * diff_x); state->v += (s3d_tri->TdVdX * diff_x); state->r += (s3d_tri->TdRdX * diff_x); state->g += (s3d_tri->TdGdX * diff_x); state->b += (s3d_tri->TdBdX * diff_x); state->a += (s3d_tri->TdAdX * diff_x); state->d += (s3d_tri->TdDdX * diff_x); state->w += (s3d_tri->TdWdX * diff_x); x = s3d_tri->clip_l; } } else { if (x < s3d_tri->clip_l) goto tri_skip_line; if (xe > s3d_tri->clip_r) goto tri_skip_line; if (xe < s3d_tri->clip_l) xe = s3d_tri->clip_l - 1; if (x > s3d_tri->clip_r) { int diff_x = x - s3d_tri->clip_r; z += (s3d_tri->TdZdX * diff_x); state->u += (s3d_tri->TdUdX * diff_x); state->v += (s3d_tri->TdVdX * diff_x); state->r += (s3d_tri->TdRdX * diff_x); state->g += (s3d_tri->TdGdX * diff_x); state->b += (s3d_tri->TdBdX * diff_x); state->a += (s3d_tri->TdAdX * diff_x); state->d += (s3d_tri->TdDdX * diff_x); state->w += (s3d_tri->TdWdX * diff_x); x = s3d_tri->clip_r; } } } virge->svga.changedvram[(dest_offset & svga->vram_mask) >> 12] = changeframecount; dest_addr = dest_offset + (x * (bpp + 1)); z_addr = z_offset + (x << 1); x &= 0xfff; xe &= 0xfff; for (; x != xe; x = (x + x_dir) & 0xfff) { int update = 1; uint16_t src_z = 0; _x = x; _y = state->y; if (use_z) { src_z = Z_READ(z_addr); Z_CLIP(src_z, z >> 16); } if (update) { uint32_t dest_col; dest_pixel(state); if (s3d_tri->cmd_set & CMD_SET_FE) { int a = state->a >> 7; state->dest_rgba.r = ((state->dest_rgba.r * a) + (s3d_tri->fog_r * (255 - a))) / 255; state->dest_rgba.g = ((state->dest_rgba.g * a) + (s3d_tri->fog_g * (255 - a))) / 255; state->dest_rgba.b = ((state->dest_rgba.b * a) + (s3d_tri->fog_b * (255 - a))) / 255; } if (s3d_tri->cmd_set & CMD_SET_ABC_ENABLE) { uint32_t src_col; int src_r = 0; uint32_t src_g = 0; uint32_t src_b = 0; switch (bpp) { case 0: /*8 bpp*/ /*Not implemented yet*/ break; case 1: /*16 bpp*/ src_col = *(uint16_t *)&vram[dest_addr & svga->vram_mask]; RGB15_TO_24(src_col, src_r, src_g, src_b); break; case 2: /*24 bpp*/ src_col = (*(uint32_t *)&vram[dest_addr & svga->vram_mask]) & 0xffffff; RGB24_TO_24(src_col, src_r, src_g, src_b); break; } state->dest_rgba.r = ((state->dest_rgba.r * state->dest_rgba.a) + (src_r * (255 - state->dest_rgba.a))) / 255; state->dest_rgba.g = ((state->dest_rgba.g * state->dest_rgba.a) + (src_g * (255 - state->dest_rgba.a))) / 255; state->dest_rgba.b = ((state->dest_rgba.b * state->dest_rgba.a) + (src_b * (255 - state->dest_rgba.a))) / 255; } switch (bpp) { case 0: /*8 bpp*/ /*Not implemented yet*/ break; case 1: /*16 bpp*/ RGB15(state->dest_rgba.r, state->dest_rgba.g, state->dest_rgba.b, dest_col); *(uint16_t *)&vram[dest_addr] = dest_col; break; case 2: /*24 bpp*/ dest_col = RGB24(state->dest_rgba.r, state->dest_rgba.g, state->dest_rgba.b); *(uint8_t *)&vram[dest_addr] = dest_col & 0xff; *(uint8_t *)&vram[dest_addr + 1] = (dest_col >> 8) & 0xff; *(uint8_t *)&vram[dest_addr + 2] = (dest_col >> 16) & 0xff; break; } if (use_z && (s3d_tri->cmd_set & CMD_SET_ZUP)) Z_WRITE(z_addr, src_z); } z += s3d_tri->TdZdX; state->u += s3d_tri->TdUdX; state->v += s3d_tri->TdVdX; state->r += s3d_tri->TdRdX; state->g += s3d_tri->TdGdX; state->b += s3d_tri->TdBdX; state->a += s3d_tri->TdAdX; state->d += s3d_tri->TdDdX; state->w += s3d_tri->TdWdX; dest_addr += x_offset; z_addr += xz_offset; virge->pixel_count++; } } tri_skip_line: state->x1 += dx1; state->x2 += dx2; state->base_u += s3d_tri->TdUdY; state->base_v += s3d_tri->TdVdY; state->base_z += s3d_tri->TdZdY; state->base_r += s3d_tri->TdRdY; state->base_g += s3d_tri->TdGdY; state->base_b += s3d_tri->TdBdY; state->base_a += s3d_tri->TdAdY; state->base_d += s3d_tri->TdDdY; state->base_w += s3d_tri->TdWdY; state->y--; dest_offset -= s3d_tri->dest_str; z_offset -= s3d_tri->z_str; } } static int tex_size[8] = {4 * 2, 2 * 2, 2 * 2, 1 * 2, 2 / 1, 2 / 1, 1 * 2, 1 * 2}; static void s3_virge_triangle(virge_t *virge, s3d_t *s3d_tri) { s3d_state_t state; uint32_t tex_base; int c; uint64_t start_time = plat_timer_read(); uint64_t end_time; state.tbu = s3d_tri->tbu << 11; state.tbv = s3d_tri->tbv << 11; state.max_d = (s3d_tri->cmd_set >> 8) & 15; state.tex_bdr_clr = s3d_tri->tex_bdr_clr; state.cmd_set = s3d_tri->cmd_set; state.base_u = s3d_tri->tus; state.base_v = s3d_tri->tvs; state.base_z = s3d_tri->tzs; state.base_r = (int32_t)s3d_tri->trs; state.base_g = (int32_t)s3d_tri->tgs; state.base_b = (int32_t)s3d_tri->tbs; state.base_a = (int32_t)s3d_tri->tas; state.base_d = s3d_tri->tds; state.base_w = s3d_tri->tws; tex_base = s3d_tri->tex_base; for (c = 9; c >= 0; c--) { state.texture[c] = (uint16_t *)&virge->svga.vram[tex_base]; if (c <= state.max_d) tex_base += ((1 << (c * 2)) * tex_size[(s3d_tri->cmd_set >> 5) & 7]) / 2; } switch ((s3d_tri->cmd_set >> 27) & 0xf) { case 0: dest_pixel = dest_pixel_gouraud_shaded_triangle; break; case 1: case 5: switch ((s3d_tri->cmd_set >> 15) & 0x3) { case 0: dest_pixel = dest_pixel_lit_texture_reflection; break; case 1: dest_pixel = dest_pixel_lit_texture_modulate; break; case 2: dest_pixel = dest_pixel_lit_texture_decal; break; default: return; } break; case 2: case 6: dest_pixel = dest_pixel_unlit_texture_triangle; break; default: return; } switch (((s3d_tri->cmd_set >> 12) & 7) | ((s3d_tri->cmd_set & (1 << 29)) ? 8 : 0)) { case 0: case 1: tex_sample = tex_sample_mipmap; break; case 2: case 3: tex_sample = virge->bilinear_enabled ? tex_sample_mipmap_filter : tex_sample_mipmap; break; case 4: case 5: tex_sample = tex_sample_normal; break; case 6: case 7: tex_sample = virge->bilinear_enabled ? tex_sample_normal_filter : tex_sample_normal; break; case (0 | 8): case (1 | 8): if ((virge->chip == S3_VIRGEDX) || (virge->chip >= S3_VIRGEGX2)) tex_sample = tex_sample_persp_mipmap_375; else tex_sample = tex_sample_persp_mipmap; break; case (2 | 8): case (3 | 8): if ((virge->chip == S3_VIRGEDX) || (virge->chip >= S3_VIRGEGX2)) tex_sample = virge->bilinear_enabled ? tex_sample_persp_mipmap_filter_375 : tex_sample_persp_mipmap_375; else tex_sample = virge->bilinear_enabled ? tex_sample_persp_mipmap_filter : tex_sample_persp_mipmap; break; case (4 | 8): case (5 | 8): if ((virge->chip == S3_VIRGEDX) || (virge->chip >= S3_VIRGEGX2)) tex_sample = tex_sample_persp_normal_375; else tex_sample = tex_sample_persp_normal; break; case (6 | 8): case (7 | 8): if ((virge->chip == S3_VIRGEDX) || (virge->chip >= S3_VIRGEGX2)) tex_sample = virge->bilinear_enabled ? tex_sample_persp_normal_filter_375 : tex_sample_persp_normal_375; else tex_sample = virge->bilinear_enabled ? tex_sample_persp_normal_filter : tex_sample_persp_normal; break; } switch ((s3d_tri->cmd_set >> 5) & 7) { case 0: tex_read = (s3d_tri->cmd_set & CMD_SET_TWE) ? tex_ARGB8888 : tex_ARGB8888_nowrap; break; case 1: tex_read = (s3d_tri->cmd_set & CMD_SET_TWE) ? tex_ARGB4444 : tex_ARGB4444_nowrap; break; case 2: tex_read = (s3d_tri->cmd_set & CMD_SET_TWE) ? tex_ARGB1555 : tex_ARGB1555_nowrap; break; default: tex_read = (s3d_tri->cmd_set & CMD_SET_TWE) ? tex_ARGB1555 : tex_ARGB1555_nowrap; break; } state.y = s3d_tri->tys; state.x1 = s3d_tri->txs; state.x2 = s3d_tri->txend01; tri(virge, s3d_tri, &state, s3d_tri->ty01, s3d_tri->TdXdY02, s3d_tri->TdXdY01); state.x2 = s3d_tri->txend12; tri(virge, s3d_tri, &state, s3d_tri->ty12, s3d_tri->TdXdY02, s3d_tri->TdXdY12); virge->tri_count++; end_time = plat_timer_read(); virge_time += end_time - start_time; } static void render_thread(void *param) { virge_t *virge = (virge_t *)param; while (virge->render_thread_run) { thread_wait_event(virge->wake_render_thread, -1); thread_reset_event(virge->wake_render_thread); virge->s3d_busy = 1; while (!RB_EMPTY) { s3_virge_triangle(virge, &virge->s3d_buffer[virge->s3d_read_idx & RB_MASK]); virge->s3d_read_idx++; if (RB_ENTRIES == (RB_SIZE - 1)) thread_set_event(virge->not_full_event); } virge->s3d_busy = 0; virge->subsys_stat |= INT_S3D_DONE; s3_virge_update_irqs(virge); } } static void queue_triangle(virge_t *virge) { if (RB_FULL) { thread_reset_event(virge->not_full_event); if (RB_FULL) thread_wait_event(virge->not_full_event, -1); /*Wait for room in ringbuffer*/ } virge->s3d_buffer[virge->s3d_write_idx & RB_MASK] = virge->s3d_tri; virge->s3d_write_idx++; if (!virge->s3d_busy) thread_set_event(virge->wake_render_thread); /*Wake up render thread if moving from idle*/ } static void s3_virge_hwcursor_draw(svga_t *svga, int displine) { virge_t *virge = (virge_t *) svga->priv; int x; uint16_t dat[2] = { 0, 0 }; int xx; int offset = svga->hwcursor_latch.x - svga->hwcursor_latch.xoff; uint32_t fg; uint32_t bg; uint32_t vram_mask = virge->vram_mask; if (svga->interlace && svga->hwcursor_oddeven) svga->hwcursor_latch.addr += 16; switch (svga->bpp) { default: if (virge->chip != S3_VIRGEGX2) { fg = svga->pallook[virge->hwc_fg_col & 0xff]; bg = svga->pallook[virge->hwc_bg_col & 0xff]; break; } fallthrough; case 15: if (virge->chip != S3_VIRGEGX2) { fg = video_15to32[virge->hwc_fg_col & 0xffff]; bg = video_15to32[virge->hwc_bg_col & 0xffff]; break; } fallthrough; case 16: if (virge->chip != S3_VIRGEGX2) { fg = video_16to32[virge->hwc_fg_col & 0xffff]; bg = video_16to32[virge->hwc_bg_col & 0xffff]; break; } fallthrough; case 24: case 32: fg = virge->hwc_fg_col; bg = virge->hwc_bg_col; break; } for (x = 0; x < 64; x += 16) { dat[0] = (svga->vram[svga->hwcursor_latch.addr & vram_mask] << 8) | svga->vram[(svga->hwcursor_latch.addr + 1) & vram_mask]; dat[1] = (svga->vram[(svga->hwcursor_latch.addr + 2) & vram_mask] << 8) | svga->vram[(svga->hwcursor_latch.addr + 3) & vram_mask]; if (svga->crtc[0x55] & 0x10) { /*X11*/ for (xx = 0; xx < 16; xx++) { if (offset >= svga->hwcursor_latch.x) { if (virge->chip == S3_VIRGEGX2) dat[0] ^= 0x8000; if (dat[0] & 0x8000) buffer32->line[displine][offset + svga->x_add] = (dat[1] & 0x8000) ? fg : bg; } offset++; dat[0] <<= 1; dat[1] <<= 1; } } else { /*Windows*/ for (xx = 0; xx < 16; xx++) { if (offset >= svga->hwcursor_latch.x) { if (!(dat[0] & 0x8000)) buffer32->line[displine][offset + svga->x_add] = (dat[1] & 0x8000) ? fg : bg; else if (dat[1] & 0x8000) buffer32->line[displine][offset + svga->x_add] ^= 0xffffff; } offset++; dat[0] <<= 1; dat[1] <<= 1; } } svga->hwcursor_latch.addr += 4; } if (svga->interlace && !svga->hwcursor_oddeven) svga->hwcursor_latch.addr += 16; } #define DECODE_YCbCr() \ do { \ int c; \ \ for (c = 0; c < 2; c++) { \ uint8_t y1, y2; \ int8_t Cr; \ int8_t Cb; \ int dR; \ int dG; \ int dB; \ \ y1 = src[0]; \ Cr = src[1] - 0x80; \ y2 = src[2]; \ Cb = src[3] - 0x80; \ src += 4; \ \ dR = (359 * Cr) >> 8; \ dG = (88 * Cb + 183 * Cr) >> 8; \ dB = (453 * Cb) >> 8; \ \ r[x_write] = y1 + dR; \ CLAMP(r[x_write]); \ g[x_write] = y1 - dG; \ CLAMP(g[x_write]); \ b[x_write] = y1 + dB; \ CLAMP(b[x_write]); \ \ r[x_write + 1] = y2 + dR; \ CLAMP(r[x_write + 1]); \ g[x_write + 1] = y2 - dG; \ CLAMP(g[x_write + 1]); \ b[x_write + 1] = y2 + dB; \ CLAMP(b[x_write + 1]); \ \ x_write = (x_write + 2) & 7; \ } \ } while (0) /*Both YUV formats are untested*/ #define DECODE_YUV211() \ do { \ uint8_t y1, y2, y3, y4; \ int8_t U, V; \ int dR; \ int dG; \ int dB; \ \ U = src[0] - 0x80; \ y1 = (298 * (src[1] - 16)) >> 8; \ y2 = (298 * (src[2] - 16)) >> 8; \ V = src[3] - 0x80; \ y3 = (298 * (src[4] - 16)) >> 8; \ y4 = (298 * (src[5] - 16)) >> 8; \ src += 6; \ \ dR = (309 * V) >> 8; \ dG = (100 * U + 208 * V) >> 8; \ dB = (516 * U) >> 8; \ \ r[x_write] = y1 + dR; \ CLAMP(r[x_write]); \ g[x_write] = y1 - dG; \ CLAMP(g[x_write]); \ b[x_write] = y1 + dB; \ CLAMP(b[x_write]); \ \ r[x_write + 1] = y2 + dR; \ CLAMP(r[x_write + 1]); \ g[x_write + 1] = y2 - dG; \ CLAMP(g[x_write + 1]); \ b[x_write + 1] = y2 + dB; \ CLAMP(b[x_write + 1]); \ \ r[x_write + 2] = y3 + dR; \ CLAMP(r[x_write + 2]); \ g[x_write + 2] = y3 - dG; \ CLAMP(g[x_write + 2]); \ b[x_write + 2] = y3 + dB; \ CLAMP(b[x_write + 2]); \ \ r[x_write + 3] = y4 + dR; \ CLAMP(r[x_write + 3]); \ g[x_write + 3] = y4 - dG; \ CLAMP(g[x_write + 3]); \ b[x_write + 3] = y4 + dB; \ CLAMP(b[x_write + 3]); \ \ x_write = (x_write + 4) & 7; \ } while (0) #define DECODE_YUV422() \ do { \ int c; \ \ for (c = 0; c < 2; c++) { \ uint8_t y1; \ uint8_t y2; \ int8_t U; \ int8_t V; \ int dR; \ int dG; \ int dB; \ \ U = src[0] - 0x80; \ y1 = (298 * (src[1] - 16)) >> 8; \ V = src[2] - 0x80; \ y2 = (298 * (src[3] - 16)) >> 8; \ src += 4; \ \ dR = (309 * V) >> 8; \ dG = (100 * U + 208 * V) >> 8; \ dB = (516 * U) >> 8; \ \ r[x_write] = y1 + dR; \ CLAMP(r[x_write]); \ g[x_write] = y1 - dG; \ CLAMP(g[x_write]); \ b[x_write] = y1 + dB; \ CLAMP(b[x_write]); \ \ r[x_write + 1] = y2 + dR; \ CLAMP(r[x_write + 1]); \ g[x_write + 1] = y2 - dG; \ CLAMP(g[x_write + 1]); \ b[x_write + 1] = y2 + dB; \ CLAMP(b[x_write + 1]); \ \ x_write = (x_write + 2) & 7; \ } \ } while (0) #define DECODE_RGB555() \ do { \ int c; \ \ for (c = 0; c < 4; c++) { \ uint16_t dat; \ \ dat = *(uint16_t *)src; \ src += 2; \ \ r[x_write + c] = \ ((dat & 0x001f) << 3) | \ ((dat & 0x001f) >> 2); \ g[x_write + c] = \ ((dat & 0x03e0) >> 2) | \ ((dat & 0x03e0) >> 7); \ b[x_write + c] = \ ((dat & 0x7c00) >> 7) | \ ((dat & 0x7c00) >> 12); \ } \ x_write = (x_write + 4) & 7; \ } while (0) #define DECODE_RGB565() \ do { \ int c; \ \ for (c = 0; c < 4; c++) { \ uint16_t dat; \ \ dat = *(uint16_t *)src; \ src += 2; \ \ r[x_write + c] = \ ((dat & 0x001f) << 3) | \ ((dat & 0x001f) >> 2); \ g[x_write + c] = \ ((dat & 0x07e0) >> 3) | \ ((dat & 0x07e0) >> 9); \ b[x_write + c] = \ ((dat & 0xf800) >> 8) | \ ((dat & 0xf800) >> 13); \ } \ x_write = (x_write + 4) & 7; \ } while (0) #define DECODE_RGB888() \ do { \ int c; \ \ for (c = 0; c < 4; c++) { \ r[x_write + c] = src[0]; \ g[x_write + c] = src[1]; \ b[x_write + c] = src[2]; \ src += 3; \ } \ x_write = (x_write + 4) & 7; \ } while (0) #define DECODE_XRGB8888() \ do { \ int c; \ \ for (c = 0; c < 4; c++) { \ r[x_write + c] = src[0]; \ g[x_write + c] = src[1]; \ b[x_write + c] = src[2]; \ src += 4; \ } \ x_write = (x_write + 4) & 7; \ } while (0) #define OVERLAY_SAMPLE() \ do { \ switch (virge->streams.sdif) { \ case 1: \ DECODE_YCbCr(); \ break; \ case 2: \ DECODE_YUV422(); \ break; \ case 3: \ DECODE_RGB555(); \ break; \ case 4: \ DECODE_YUV211(); \ break; \ case 5: \ DECODE_RGB565(); \ break; \ case 6: \ DECODE_RGB888(); \ break; \ case 7: \ default: \ DECODE_XRGB8888(); \ break; \ } \ } while (0) static void s3_virge_overlay_draw(svga_t *svga, int displine) { virge_t *virge = (virge_t *) svga->priv; int offset = (virge->streams.sec_x - virge->streams.pri_x) + 1; int h_acc = virge->streams.dda_horiz_accumulator; int r[8]; int g[8]; int b[8]; int x_size; int x_read = 4; int x_write = 4; int x; uint32_t *p; uint8_t *src = &svga->vram[svga->overlay_latch.addr]; p = &((uint32_t *)buffer32->line[displine])[offset + svga->x_add]; if ((offset + virge->streams.sec_w) > virge->streams.pri_w) x_size = (virge->streams.pri_w - virge->streams.sec_x) + 1; else x_size = virge->streams.sec_w + 1; OVERLAY_SAMPLE(); for (x = 0; x < x_size; x++) { *p++ = r[x_read] | (g[x_read] << 8) | (b[x_read] << 16); h_acc += virge->streams.k1_horiz_scale; if (h_acc >= 0) { if ((x_read ^ (x_read + 1)) & ~3) OVERLAY_SAMPLE(); x_read = (x_read + 1) & 7; h_acc += (virge->streams.k2_horiz_scale - virge->streams.k1_horiz_scale); } } svga->overlay_latch.v_acc += virge->streams.k1_vert_scale; if (svga->overlay_latch.v_acc >= 0) { svga->overlay_latch.v_acc += (virge->streams.k2_vert_scale - virge->streams.k1_vert_scale); svga->overlay_latch.addr += virge->streams.sec_stride; } } static uint8_t s3_virge_pci_read(UNUSED(int func), int addr, void *priv) { const virge_t *virge = (virge_t *) priv; const svga_t *svga = &virge->svga; uint8_t ret = 0; switch (addr) { case 0x00: ret = 0x33; break; /*'S3'*/ case 0x01: ret = 0x53; break; case 0x02: ret = virge->virge_id_low; break; case 0x03: ret = virge->virge_id_high; break; case PCI_REG_COMMAND: ret = virge->pci_regs[PCI_REG_COMMAND] & 0x27; break; case 0x07: ret = virge->pci_regs[0x07] & 0x36; break; case 0x08: ret = virge->virge_rev; break; /*Revision ID*/ case 0x09: ret = 0; break; /*Programming interface*/ case 0x0a: ret = 0x00; break; /*Supports VGA interface*/ case 0x0b: ret = 0x03; break; case 0x0d: ret = virge->pci_regs[0x0d] & 0xf8; break; case 0x10: ret = 0x00; break; /*Linear frame buffer address*/ case 0x11: ret = 0x00; break; case 0x12: ret = 0x00; break; case 0x13: ret = (virge->chip == S3_VIRGEVX || virge->chip == S3_TRIO3D2X) ? (svga->crtc[0x59] & 0xfe) : (svga->crtc[0x59] & 0xfc); break; case 0x2c: ret = 0x33; break; /* Subsystem vendor ID */ case 0x2d: ret = 0x53; break; case 0x2e: ret = virge->virge_id_low; break; case 0x2f: ret = virge->virge_id_high; break; case 0x30: ret = (!virge->onboard) ? (virge->pci_regs[0x30] & 0x01) : 0x00; break; /*BIOS ROM address*/ case 0x31: ret = 0x00; break; case 0x32: ret = (!virge->onboard) ? virge->pci_regs[0x32] : 0x00; break; case 0x33: ret = (!virge->onboard) ? virge->pci_regs[0x33] : 0x00; break; case 0x34: ret = (virge->chip >= S3_VIRGEGX2) ? 0xdc : 0x00; break; case 0x3c: ret = virge->pci_regs[0x3c]; break; case 0x3d: ret = PCI_INTA; break; /*INTA*/ case 0x3e: ret = 0x04; break; case 0x3f: ret = 0xff; break; case 0x80: ret = 0x02; break; /* AGP capability */ case 0x81: ret = 0x00; break; case 0x82: ret = 0x10; break; /* assumed AGP 1.0 */ case 0x84: ret = (virge->chip >= S3_TRIO3D2X) ? 0x03 : 0x01; break; case 0x87: ret = 0x1f; break; case 0x88: ret = virge->pci_regs[0x88]; break; case 0x89: ret = virge->pci_regs[0x89]; break; case 0x8a: ret = virge->pci_regs[0x8a]; break; case 0x8b: ret = virge->pci_regs[0x8b]; break; case 0xdc: ret = 0x01; break; /* PCI Power Management capability */ case 0xdd: ret = virge->is_agp ? 0x80 : 0x00; break; case 0xde: ret = 0x21; break; case 0xe0: ret = virge->pci_regs[0xe0]; break; case 0xe1: ret = virge->pci_regs[0xe1]; break; case 0xe2: ret = virge->pci_regs[0xe2]; break; case 0xe3: ret = virge->pci_regs[0xe3]; break; default: break; } return ret; } static void s3_virge_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) { virge_t *virge = (virge_t *) priv; svga_t *svga = &virge->svga; switch (addr) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x3d: case 0x3e: case 0x3f: return; case PCI_REG_COMMAND: if (val & PCI_COMMAND_IO) { io_removehandler(0x03c0, 0x0020, s3_virge_in, NULL, NULL, s3_virge_out, NULL, NULL, virge); io_sethandler(0x03c0, 0x0020, s3_virge_in, NULL, NULL, s3_virge_out, NULL, NULL, virge); } else io_removehandler(0x03c0, 0x0020, s3_virge_in, NULL, NULL, s3_virge_out, NULL, NULL, virge); virge->pci_regs[PCI_REG_COMMAND] = val & 0x27; s3_virge_updatemapping(virge); return; case 0x07: virge->pci_regs[0x07] = val & 0x3e; return; case 0x0d: virge->pci_regs[0x0d] = val & 0xf8; return; case 0x13: svga->crtc[0x59] = (virge->chip == S3_VIRGEVX || virge->chip == S3_TRIO3D2X) ? (val & 0xfe) : (val & 0xfc); s3_virge_updatemapping(virge); return; case 0x30: case 0x32: case 0x33: if (virge->onboard) return; virge->pci_regs[addr] = val; if (virge->pci_regs[0x30] & 0x01) { uint32_t biosaddr = (virge->pci_regs[0x32] << 16) | (virge->pci_regs[0x33] << 24); if (virge->chip == S3_VIRGEGX2) mem_mapping_set_addr(&virge->bios_rom.mapping, biosaddr, 0x10000); else mem_mapping_set_addr(&virge->bios_rom.mapping, biosaddr, 0x8000); } else { mem_mapping_disable(&virge->bios_rom.mapping); } return; case 0x3c: virge->pci_regs[0x3c] = val; return; case 0x88: virge->pci_regs[0x88] = val & 0x27; return; case 0x89: virge->pci_regs[0x89] = val & 0x03; return; case 0x8b: case 0xe1: case 0xe3: virge->pci_regs[addr] = val; return; case 0xe0: virge->pci_regs[0xe0] = val & 0x03; return; case 0xe2: virge->pci_regs[0xe2] = val & 0xc0; return; default: break; } } static void s3_virge_disable_handlers(virge_t *dev) { io_removehandler(0x03c0, 0x0020, s3_virge_in, NULL, NULL, s3_virge_out, NULL, NULL, dev); mem_mapping_disable(&dev->linear_mapping); mem_mapping_disable(&dev->mmio_mapping); mem_mapping_disable(&dev->new_mmio_mapping); mem_mapping_disable(&dev->svga.mapping); mem_mapping_disable(&dev->bios_rom.mapping); /* Save all the mappings and the timers because they are part of linked lists. */ reset_state->linear_mapping = dev->linear_mapping; reset_state->mmio_mapping = dev->mmio_mapping; reset_state->new_mmio_mapping = dev->new_mmio_mapping; reset_state->svga.mapping = dev->svga.mapping; reset_state->bios_rom.mapping = dev->bios_rom.mapping; reset_state->svga.timer = dev->svga.timer; reset_state->svga.timer8514 = dev->svga.timer8514; } static void s3_virge_reset(void *priv) { virge_t *dev = (virge_t *) priv; if (reset_state != NULL) { s3_virge_disable_handlers(dev); dev->virge_busy = 0; dev->fifo_write_idx = 0; dev->fifo_read_idx = 0; dev->s3d_busy = 0; dev->s3d_write_idx = 0; dev->s3d_read_idx = 0; reset_state->pci_slot = dev->pci_slot; *dev = *reset_state; } } static void * s3_virge_init(const device_t *info) { const char *bios_fn; virge_t *virge = (virge_t *) calloc(1, sizeof(virge_t)); reset_state = calloc(1, sizeof(virge_t)); virge->bilinear_enabled = device_get_config_int("bilinear"); virge->dithering_enabled = device_get_config_int("dithering"); if (info->local >= S3_VIRGE_GX2) virge->memory_size = 4; else virge->memory_size = device_get_config_int("memory"); virge->onboard = !!(info->local & 0x100); switch (info->local) { case S3_VIRGE_325: bios_fn = ROM_VIRGE_325; break; case S3_DIAMOND_STEALTH3D_2000: bios_fn = ROM_DIAMOND_STEALTH3D_2000; break; case S3_DIAMOND_STEALTH3D_3000: bios_fn = ROM_DIAMOND_STEALTH3D_3000; break; case S3_STB_VELOCITY_3D: bios_fn = ROM_STB_VELOCITY_3D; break; case S3_VIRGE_DX: bios_fn = virge->onboard ? NULL : ROM_VIRGE_DX; break; case S3_DIAMOND_STEALTH3D_2000PRO: bios_fn = ROM_DIAMOND_STEALTH3D_2000PRO; break; case S3_VIRGE_GX: bios_fn = ROM_VIRGE_GX; break; case S3_VIRGE_GX2: bios_fn = ROM_VIRGE_GX2; break; case S3_DIAMOND_STEALTH3D_4000: bios_fn = ROM_DIAMOND_STEALTH3D_4000; break; case S3_TRIO_3D2X: bios_fn = ROM_TRIO3D2X; break; default: free(virge); return NULL; } svga_init(info, &virge->svga, virge, virge->memory_size << 20, s3_virge_recalctimings, s3_virge_in, s3_virge_out, s3_virge_hwcursor_draw, s3_virge_overlay_draw); virge->svga.hwcursor.cur_ysize = 64; if (bios_fn != NULL) { if (info->local == S3_VIRGE_GX2) rom_init(&virge->bios_rom, bios_fn, 0xc0000, 0x10000, 0xffff, 0, MEM_MAPPING_EXTERNAL); else rom_init(&virge->bios_rom, bios_fn, 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); mem_mapping_disable(&virge->bios_rom.mapping); } mem_mapping_add(&virge->linear_mapping, 0, 0, svga_read_linear, svga_readw_linear, svga_readl_linear, svga_write_linear, svga_writew_linear, svga_writel_linear, NULL, MEM_MAPPING_EXTERNAL, &virge->svga); mem_mapping_add(&virge->mmio_mapping, 0, 0, s3_virge_mmio_read, s3_virge_mmio_read_w, s3_virge_mmio_read_l, s3_virge_mmio_write, s3_virge_mmio_write_w, s3_virge_mmio_write_l, NULL, MEM_MAPPING_EXTERNAL, virge); mem_mapping_add(&virge->new_mmio_mapping, 0, 0, s3_virge_mmio_read, s3_virge_mmio_read_w, s3_virge_mmio_read_l, s3_virge_mmio_write, s3_virge_mmio_write_w, s3_virge_mmio_write_l, NULL, MEM_MAPPING_EXTERNAL, virge); io_sethandler(0x03c0, 0x0020, s3_virge_in, NULL, NULL, s3_virge_out, NULL, NULL, virge); virge->pci_regs[PCI_REG_COMMAND] = 3; virge->pci_regs[0x05] = 0; virge->pci_regs[0x06] = 0; virge->pci_regs[0x07] = 2; virge->pci_regs[0x32] = 0x0c; virge->pci_regs[0x3d] = 1; virge->pci_regs[0x3e] = 4; virge->pci_regs[0x3f] = 0xff; virge->virge_rev = 0; virge->virge_id = 0xe1; virge->is_agp = !!(info->flags & DEVICE_AGP); switch (info->local) { case S3_VIRGE_325: case S3_DIAMOND_STEALTH3D_2000: virge->fifo_slots_num = 8; virge->svga.decode_mask = (4 << 20) - 1; virge->virge_id_high = 0x56; virge->virge_id_low = 0x31; virge->svga.crtc[0x59] = 0x70; virge->chip = S3_VIRGE; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_diamond_stealth3d_2000_pci); break; case S3_DIAMOND_STEALTH3D_3000: case S3_STB_VELOCITY_3D: virge->fifo_slots_num = 8; virge->svga.decode_mask = (8 << 20) - 1; virge->virge_id_high = 0x88; virge->virge_id_low = 0x3d; virge->svga.crtc[0x59] = 0x70; virge->chip = S3_VIRGEVX; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_diamond_stealth3d_3000_pci); break; case S3_VIRGE_GX2: case S3_DIAMOND_STEALTH3D_4000: virge->fifo_slots_num = 16; virge->svga.decode_mask = (4 << 20) - 1; virge->virge_id_high = 0x8a; virge->virge_id_low = 0x10; virge->svga.crtc[0x6c] = 1; virge->svga.crtc[0x59] = 0x70; virge->svga.vblank_start = s3_virge_vblank_start; virge->chip = S3_VIRGEGX2; video_inform(VIDEO_FLAG_TYPE_SPECIAL, virge->is_agp ? &timing_virge_agp : &timing_virge_dx_pci); break; case S3_TRIO_3D2X: virge->fifo_slots_num = 16; virge->svga.decode_mask = (8 << 20) - 1; virge->virge_id_high = 0x8a; virge->virge_id_low = 0x13; virge->virge_rev = 0x01; virge->svga.crtc[0x6c] = 1; virge->svga.crtc[0x59] = 0x70; virge->svga.vblank_start = s3_virge_vblank_start; virge->chip = S3_TRIO3D2X; video_inform(VIDEO_FLAG_TYPE_SPECIAL, virge->is_agp ? &timing_virge_agp : &timing_virge_dx_pci); break; case S3_VIRGE_GX: virge->virge_rev = 0x01; fallthrough; default: virge->fifo_slots_num = 8; virge->svga.decode_mask = (4 << 20) - 1; virge->virge_id_high = 0x8a; virge->virge_id_low = 0x01; virge->svga.crtc[0x6c] = 1; virge->svga.crtc[0x59] = 0x70; virge->svga.vblank_start = s3_virge_vblank_start; virge->chip = S3_VIRGEDX; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_virge_dx_pci); break; } if (virge->chip == S3_VIRGEGX2) { virge->vram_mask = (4 << 20) - 1; virge->svga.vram_mask = (4 << 20) - 1; virge->svga.vram_max = 4 << 20; virge->svga.crtc[0x36] = 2 | (2 << 2) | (1 << 4) | (1 << 5); } else { switch (virge->memory_size) { case 2: virge->vram_mask = (2 << 20) - 1; virge->svga.vram_mask = (2 << 20) - 1; virge->svga.vram_max = 2 << 20; if (virge->chip == S3_VIRGEVX) { virge->svga.crtc[0x36] = (0 << 5); } else virge->svga.crtc[0x36] = 2 | (0 << 2) | (1 << 4) | (4 << 5); break; case 8: virge->vram_mask = (8 << 20) - 1; virge->svga.vram_mask = (8 << 20) - 1; virge->svga.vram_max = 8 << 20; if (virge->chip == S3_TRIO3D2X) virge->svga.crtc[0x36] = 2 | (2 << 2) | (1 << 4) | (0 << 5); else virge->svga.crtc[0x36] = (3 << 5); break; case 4: virge->vram_mask = (4 << 20) - 1; virge->svga.vram_mask = (4 << 20) - 1; virge->svga.vram_max = 4 << 20; if (virge->chip == S3_VIRGEVX) virge->svga.crtc[0x36] = (1 << 5); else if (virge->chip == S3_TRIO3D2X) virge->svga.crtc[0x36] = 2 | (2 << 2) | (1 << 4) | (2 << 5); else virge->svga.crtc[0x36] = 2 | (0 << 2) | (1 << 4) | (0 << 5); break; default: break; } if (info->local == S3_VIRGE_GX) virge->svga.crtc[0x36] |= (1 << 2); } virge->svga.crtc[0x37] = 1 | (7 << 5); virge->svga.crtc[0x53] = 8; if (bios_fn == NULL) pci_add_card(virge->is_agp ? PCI_ADD_AGP : PCI_ADD_VIDEO, s3_virge_pci_read, s3_virge_pci_write, virge, &virge->pci_slot); else pci_add_card(virge->is_agp ? PCI_ADD_AGP : PCI_ADD_NORMAL, s3_virge_pci_read, s3_virge_pci_write, virge, &virge->pci_slot); virge->i2c = i2c_gpio_init("ddc_s3_virge"); virge->ddc = ddc_init(i2c_gpio_get_bus(virge->i2c)); virge->svga.force_old_addr = 1; virge->render_thread_run = 1; virge->wake_render_thread = thread_create_event(); virge->wake_main_thread = thread_create_event(); virge->not_full_event = thread_create_event(); virge->render_thread = thread_create(render_thread, virge); virge->fifo_thread_run = 1; virge->wake_fifo_thread = thread_create_event(); virge->fifo_not_full_event = thread_create_event(); virge->fifo_thread = thread_create(fifo_thread, virge); virge->local = info->local; *reset_state = *virge; return virge; } static void s3_virge_close(void *priv) { virge_t *virge = (virge_t *) priv; virge->render_thread_run = 0; thread_set_event(virge->wake_render_thread); thread_wait(virge->render_thread); thread_destroy_event(virge->not_full_event); thread_destroy_event(virge->wake_main_thread); thread_destroy_event(virge->wake_render_thread); virge->fifo_thread_run = 0; thread_set_event(virge->wake_fifo_thread); thread_wait(virge->fifo_thread); thread_destroy_event(virge->fifo_not_full_event); thread_destroy_event(virge->wake_fifo_thread); svga_close(&virge->svga); ddc_close(virge->ddc); i2c_gpio_close(virge->i2c); free(reset_state); reset_state = NULL; free(virge); } static int s3_virge_325_diamond_available(void) { return rom_present(ROM_DIAMOND_STEALTH3D_2000); } static int s3_virge_325_available(void) { return rom_present(ROM_VIRGE_325); } static int s3_virge_988_diamond_available(void) { return rom_present(ROM_DIAMOND_STEALTH3D_3000); } static int s3_virge_988_stb_available(void) { return rom_present(ROM_STB_VELOCITY_3D); } static int s3_virge_375_available(void) { return rom_present(ROM_VIRGE_DX); } static int s3_virge_375_diamond_available(void) { return rom_present(ROM_DIAMOND_STEALTH3D_2000PRO); } static int s3_virge_385_available(void) { return rom_present(ROM_VIRGE_GX); } static int s3_virge_357_available(void) { return rom_present(ROM_VIRGE_GX2); } static int s3_virge_357_diamond_available(void) { return rom_present(ROM_DIAMOND_STEALTH3D_4000); } static int s3_trio3d2x_available(void) { return rom_present(ROM_TRIO3D2X); } static void s3_virge_speed_changed(void *priv) { virge_t *virge = (virge_t *) priv; svga_recalctimings(&virge->svga); } static void s3_virge_force_redraw(void *priv) { virge_t *virge = (virge_t *) priv; virge->svga.fullchange = changeframecount; } static const device_config_t s3_virge_config[] = { // clang-format off { .name = "memory", .description = "Memory size", .type = CONFIG_SELECTION, .default_int = 4, .selection = { { .description = "2 MB", .value = 2 }, { .description = "4 MB", .value = 4 }, { .description = "" } } }, { .name = "bilinear", .description = "Bilinear filtering", .type = CONFIG_BINARY, .default_int = 1 }, { .name = "dithering", .description = "Dithering", .type = CONFIG_BINARY, .default_int = 1 }, { .type = CONFIG_END } // clang-format on }; static const device_config_t s3_virge_stb_config[] = { // clang-format off { .name = "memory", .description = "Memory size", .type = CONFIG_SELECTION, .default_int = 4, .selection = { { .description = "2 MB", .value = 2 }, { .description = "4 MB", .value = 4 }, { .description = "8 MB", .value = 8 }, { .description = "" } } }, { .name = "bilinear", .description = "Bilinear filtering", .type = CONFIG_BINARY, .default_int = 1 }, { .name = "dithering", .description = "Dithering", .type = CONFIG_BINARY, .default_int = 1 }, { .type = CONFIG_END } // clang-format on }; static const device_config_t s3_virge_357_config[] = { // clang-format off { .name = "bilinear", .description = "Bilinear filtering", .type = CONFIG_BINARY, .default_int = 1 }, { .name = "dithering", .description = "Dithering", .type = CONFIG_BINARY, .default_int = 1 }, { .type = CONFIG_END } // clang-format on }; static const device_config_t s3_trio3d2x_config[] = { // clang-format off { .name = "memory", .description = "Memory size", .type = CONFIG_SELECTION, .default_int = 4, .selection = { { .description = "4 MB", .value = 4 }, { .description = "8 MB", .value = 8 }, { .description = "" } } }, { .name = "bilinear", .description = "Bilinear filtering", .type = CONFIG_BINARY, .default_int = 1 }, { .name = "dithering", .description = "Dithering", .type = CONFIG_BINARY, .default_int = 1 }, { .type = CONFIG_END } // clang-format on }; const device_t s3_virge_325_pci_device = { .name = "S3 ViRGE (325) PCI", .internal_name = "virge325_pci", .flags = DEVICE_PCI, .local = S3_VIRGE_325, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_virge_325_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_config }; const device_t s3_diamond_stealth_2000_pci_device = { .name = "S3 ViRGE (Diamond Stealth 3D 2000) PCI", .internal_name = "stealth3d_2000_pci", .flags = DEVICE_PCI, .local = S3_DIAMOND_STEALTH3D_2000, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_virge_325_diamond_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_config }; const device_t s3_diamond_stealth_3000_pci_device = { .name = "S3 ViRGE/VX (Diamond Stealth 3D 3000) PCI", .internal_name = "stealth3d_3000_pci", .flags = DEVICE_PCI, .local = S3_DIAMOND_STEALTH3D_3000, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_virge_988_diamond_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_stb_config }; const device_t s3_stb_velocity_3d_pci_device = { .name = "S3 ViRGE/VX (STB Velocity 3D) PCI", .internal_name = "stb_velocity3d_pci", .flags = DEVICE_PCI, .local = S3_STB_VELOCITY_3D, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_virge_988_stb_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_stb_config }; const device_t s3_virge_375_pci_device = { .name = "S3 ViRGE/DX (375) PCI", .internal_name = "virge375_pci", .flags = DEVICE_PCI, .local = S3_VIRGE_DX, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_virge_375_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_config }; const device_t s3_virge_375_onboard_pci_device = { .name = "S3 ViRGE/DX (375) On-Board PCI", .internal_name = "virge375_onboard_pci", .flags = DEVICE_PCI, .local = S3_VIRGE_DX | 0x100, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = NULL }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_config }; const device_t s3_diamond_stealth_2000pro_pci_device = { .name = "S3 ViRGE/DX (Diamond Stealth 3D 2000 Pro) PCI", .internal_name = "stealth3d_2000pro_pci", .flags = DEVICE_PCI, .local = S3_DIAMOND_STEALTH3D_2000PRO, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_virge_375_diamond_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_config }; const device_t s3_virge_385_pci_device = { .name = "S3 ViRGE/GX (385) PCI", .internal_name = "virge385_pci", .flags = DEVICE_PCI, .local = S3_VIRGE_GX, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_virge_385_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_config }; const device_t s3_virge_357_pci_device = { .name = "S3 ViRGE/GX2 (357) PCI", .internal_name = "virge357_pci", .flags = DEVICE_PCI, .local = S3_VIRGE_GX2, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_virge_357_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_357_config }; const device_t s3_virge_357_agp_device = { .name = "S3 ViRGE/GX2 (357) AGP", .internal_name = "virge357_agp", .flags = DEVICE_AGP, .local = S3_VIRGE_GX2, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_virge_357_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_357_config }; const device_t s3_diamond_stealth_4000_pci_device = { .name = "S3 ViRGE/GX2 (Diamond Stealth 3D 4000) PCI", .internal_name = "stealth3d_4000_pci", .flags = DEVICE_PCI, .local = S3_DIAMOND_STEALTH3D_4000, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_virge_357_diamond_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_357_config }; const device_t s3_diamond_stealth_4000_agp_device = { .name = "S3 ViRGE/GX2 (Diamond Stealth 3D 4000) AGP", .internal_name = "stealth3d_4000_agp", .flags = DEVICE_AGP, .local = S3_DIAMOND_STEALTH3D_4000, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_virge_357_diamond_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_virge_357_config }; const device_t s3_trio3d2x_pci_device = { .name = "S3 Trio3D/2X (362) PCI", .internal_name = "trio3d2x", .flags = DEVICE_PCI, .local = S3_TRIO_3D2X, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_trio3d2x_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_trio3d2x_config }; const device_t s3_trio3d2x_agp_device = { .name = "S3 Trio3D/2X (362) AGP", .internal_name = "trio3d2x_agp", .flags = DEVICE_AGP, .local = S3_TRIO_3D2X, .init = s3_virge_init, .close = s3_virge_close, .reset = s3_virge_reset, { .available = s3_trio3d2x_available }, .speed_changed = s3_virge_speed_changed, .force_redraw = s3_virge_force_redraw, .config = s3_trio3d2x_config };