/* * 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 emulation. * * * * Authors: Sarah Walker, * Miran Grca, * * Copyright 2008-2019 Sarah Walker. * Copyright 2016-2019 Miran Grca. */ #include #include #include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/device.h> #include <86box/io.h> #include <86box/timer.h> #include <86box/mem.h> #include <86box/pci.h> #include <86box/rom.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> #include "cpu.h" #define ROM_ORCHID_86C911 "roms/video/s3/BIOS.BIN" #define ROM_DIAMOND_STEALTH_VRAM "roms/video/s3/Diamond Stealth VRAM BIOS v2.31 U14.BIN" #define ROM_AMI_86C924 "roms/video/s3/S3924AMI.BIN" #define ROM_METHEUS_86C928 "roms/video/s3/928.VBI" #define ROM_SPEA_MERCURY_LITE_PCI "roms/video/s3/SPEAVGA.VBI" #define ROM_SPEA_MIRAGE_86C801 "roms/video/s3/V7MIRAGE.VBI" #define ROM_SPEA_MIRAGE_86C805 "roms/video/s3/86c805pspeavlbus.BIN" #define ROM_MIROCRYSTAL8S_805 "roms/video/s3/S3_805VL_ATT20C491_miroCRYSTAL_8s_ver1.4.BIN" #define ROM_MIROCRYSTAL10SD_805 "roms/video/s3/MIROcrystal10SD_VLB.VBI" #define ROM_MIROCRYSTAL20SV_964_VLB "roms/video/s3/S3_964VL_BT485_27C256_miroCRYSTAL_20sv_ver1.2.bin" #define ROM_MIROCRYSTAL20SV_964_PCI "roms/video/s3/mirocrystal.VBI" #define ROM_MIROCRYSTAL20SD_864_VLB "roms/video/s3/Miro20SD.BIN" #define ROM_PHOENIX_86C80X "roms/video/s3/805.VBI" #define ROM_PARADISE_BAHAMAS64 "roms/video/s3/bahamas64.bin" #define ROM_PHOENIX_VISION864 "roms/video/s3/86c864p.bin" #define ROM_DIAMOND_STEALTH64_964 "roms/video/s3/964_107h.rom" #define ROM_PHOENIX_TRIO32 "roms/video/s3/86c732p.bin" #define ROM_SPEA_MIRAGE_P64 "roms/video/s3/S3_764VL_SPEAMirageP64VL_ver5_03.BIN" #define ROM_NUMBER9_9FX "roms/video/s3/s3_764.bin" #define ROM_PHOENIX_TRIO64 "roms/video/s3/86c764x1.bin" #define ROM_DIAMOND_STEALTH64_764 "roms/video/s3/stealt64.bin" #define ROM_TRIO64V2_DX_VBE20 "roms/video/s3/86c775_2.bin" #define ROM_STB_POWERGRAPH_64_VIDEO "roms/video/s3/VBIOS.BIN" #define ROM_PHOENIX_TRIO64VPLUS "roms/video/s3/64V1506.ROM" #define ROM_CARDEX_TRIO64VPLUS "roms/video/s3/S3T64VP.VBI" #define ROM_DIAMOND_STEALTH_SE "roms/video/s3/DiamondStealthSE.VBI" #define ROM_ELSAWIN2KPROX_964 "roms/video/s3/elsaw20004m.BIN" #define ROM_ELSAWIN2KPROX "roms/video/s3/elsaw20008m.BIN" #define ROM_NUMBER9_9FX_531 "roms/video/s3/numbernine.BIN" #define ROM_PHOENIX_VISION868 "roms/video/s3/1-DSV3868.BIN" #define ROM_MIROVIDEO40SV_ERGO_968_PCI "roms/video/s3/S3_968PCI_TVP3026_miroVideo40SV_PCI_1.04.BIN" #define ROM_SPEA_MERCURY_P64V "roms/video/s3/S3_968PCI_TVP3026_SPEAMecuryP64V_ver1.01.BIN" #define ROM_NUMBER9_9FX_771 "roms/video/s3/no9motionfx771.BIN" #define ROM_PHOENIX_VISION968 "roms/video/s3/1-DSV3968P.BIN" enum { S3_NUMBER9_9FX, S3_PARADISE_BAHAMAS64, S3_DIAMOND_STEALTH64_964, S3_PHOENIX_TRIO32, S3_PHOENIX_TRIO32_ONBOARD, S3_PHOENIX_TRIO64, S3_PHOENIX_TRIO64_ONBOARD, S3_PHOENIX_VISION864, S3_DIAMOND_STEALTH64_764, S3_SPEA_MIRAGE_86C801, S3_SPEA_MIRAGE_86C805, S3_PHOENIX_86C801, S3_PHOENIX_86C805, S3_ORCHID_86C911, S3_METHEUS_86C928, S3_AMI_86C924, S3_TRIO64V2_DX, S3_TRIO64V2_DX_ONBOARD, S3_STB_POWERGRAPH_64_VIDEO, S3_PHOENIX_TRIO64VPLUS, S3_PHOENIX_TRIO64VPLUS_ONBOARD, S3_CARDEX_TRIO64VPLUS, S3_DIAMOND_STEALTH_SE, S3_DIAMOND_STEALTH_VRAM, S3_ELSAWIN2KPROX_964, S3_ELSAWIN2KPROX, S3_PHOENIX_VISION868, S3_MIROVIDEO40SV_ERGO_968, S3_MIROCRYSTAL10SD_805, S3_SPEA_MIRAGE_P64, S3_SPEA_MERCURY_P64V, S3_MIROCRYSTAL20SV_964, S3_MIROCRYSTAL20SD_864, S3_PHOENIX_VISION968, S3_MIROCRYSTAL8S_805, S3_NUMBER9_9FX_531, S3_NUMBER9_9FX_771, S3_SPEA_MERCURY_LITE_PCI, S3_86C805_ONBOARD }; enum { S3_86C911 = 0x00, S3_86C924 = 0x02, S3_86C928 = 0x04, S3_86C928PCI = 0x06, S3_86C801 = 0x07, S3_86C805 = 0x08, S3_VISION964 = 0x18, S3_VISION968 = 0x20, S3_VISION864 = 0x28, S3_VISION868 = 0x30, S3_TRIO32 = 0x38, S3_TRIO64 = 0x40, S3_TRIO64V = 0x48, S3_TRIO64V2 = 0x50 }; static video_timings_t timing_s3_86c911 = { .type = VIDEO_ISA, .write_b = 4, .write_w = 4, .write_l = 5, .read_b = 20, .read_w = 20, .read_l = 35 }; static video_timings_t timing_s3_86c801 = { .type = VIDEO_ISA, .write_b = 4, .write_w = 4, .write_l = 5, .read_b = 20, .read_w = 20, .read_l = 35 }; static video_timings_t timing_s3_86c805 = { .type = VIDEO_BUS, .write_b = 4, .write_w = 4, .write_l = 5, .read_b = 20, .read_w = 20, .read_l = 35 }; static video_timings_t timing_s3_86c928pci = { .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_s3_stealth64_vlb = { .type = VIDEO_BUS, .write_b = 2, .write_w = 2, .write_l = 4, .read_b = 26, .read_w = 26, .read_l = 42 }; static video_timings_t timing_s3_stealth64_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_s3_vision864_vlb = { .type = VIDEO_BUS, .write_b = 4, .write_w = 4, .write_l = 5, .read_b = 20, .read_w = 20, .read_l = 35 }; static video_timings_t timing_s3_vision864_pci = { .type = VIDEO_PCI, .write_b = 4, .write_w = 4, .write_l = 5, .read_b = 20, .read_w = 20, .read_l = 35 }; static video_timings_t timing_s3_vision868_vlb = { .type = VIDEO_BUS, .write_b = 4, .write_w = 4, .write_l = 5, .read_b = 20, .read_w = 20, .read_l = 35 }; static video_timings_t timing_s3_vision868_pci = { .type = VIDEO_PCI, .write_b = 4, .write_w = 4, .write_l = 5, .read_b = 20, .read_w = 20, .read_l = 35 }; static video_timings_t timing_s3_vision964_vlb = { .type = VIDEO_BUS, .write_b = 2, .write_w = 2, .write_l = 4, .read_b = 20, .read_w = 20, .read_l = 35 }; static video_timings_t timing_s3_vision964_pci = { .type = VIDEO_PCI, .write_b = 2, .write_w = 2, .write_l = 4, .read_b = 20, .read_w = 20, .read_l = 35 }; static video_timings_t timing_s3_vision968_vlb = { .type = VIDEO_BUS, .write_b = 2, .write_w = 2, .write_l = 4, .read_b = 20, .read_w = 20, .read_l = 35 }; static video_timings_t timing_s3_vision968_pci = { .type = VIDEO_PCI, .write_b = 2, .write_w = 2, .write_l = 4, .read_b = 20, .read_w = 20, .read_l = 35 }; static video_timings_t timing_s3_trio32_vlb = { .type = VIDEO_BUS, .write_b = 4, .write_w = 3, .write_l = 5, .read_b = 26, .read_w = 26, .read_l = 42 }; static video_timings_t timing_s3_trio32_pci = { .type = VIDEO_PCI, .write_b = 4, .write_w = 3, .write_l = 5, .read_b = 26, .read_w = 26, .read_l = 42 }; static video_timings_t timing_s3_trio64_vlb = { .type = VIDEO_BUS, .write_b = 3, .write_w = 2, .write_l = 4, .read_b = 25, .read_w = 25, .read_l = 40 }; static video_timings_t timing_s3_trio64_pci = { .type = VIDEO_PCI, .write_b = 3, .write_w = 2, .write_l = 4, .read_b = 25, .read_w = 25, .read_l = 40 }; static video_timings_t timing_s3_trio64vp_cardex_pci = { .type = VIDEO_PCI, .write_b = 2, .write_w = 2, .write_l = 3, .read_b = 19, .read_w = 19, .read_l = 30 }; enum { VRAM_4MB = 0, VRAM_8MB = 3, VRAM_2MB = 4, VRAM_1MB = 6, VRAM_512KB = 7 }; #define FIFO_SIZE 65536 #define FIFO_MASK (FIFO_SIZE - 1) #define FIFO_ENTRY_SIZE (1 << 31) #define FIFO_ENTRIES (s3->fifo_write_idx - s3->fifo_read_idx) #define FIFO_FULL ((s3->fifo_write_idx - s3->fifo_read_idx) >= (FIFO_SIZE - 4)) #define FIFO_EMPTY (s3->fifo_read_idx == s3->fifo_write_idx) #define FIFO_TYPE 0xff000000 #define FIFO_ADDR 0x00ffffff enum { FIFO_INVALID = (0x00 << 24), FIFO_WRITE_BYTE = (0x01 << 24), FIFO_WRITE_WORD = (0x02 << 24), FIFO_WRITE_DWORD = (0x03 << 24), FIFO_OUT_BYTE = (0x04 << 24), FIFO_OUT_WORD = (0x05 << 24), FIFO_OUT_DWORD = (0x06 << 24) }; typedef struct { uint32_t addr_type; uint32_t val; } fifo_entry_t; typedef struct s3_t { mem_mapping_t linear_mapping; mem_mapping_t mmio_mapping; mem_mapping_t new_mmio_mapping; uint8_t has_bios; rom_t bios_rom; svga_t svga; uint8_t bank; uint8_t ma_ext; int width, bpp; int chip; int pci, vlb; int atbus; uint8_t id, id_ext, id_ext_pci; uint8_t int_line; int packed_mmio; uint32_t linear_base, linear_size; uint8_t pci_regs[256]; uint8_t pci_slot; uint8_t irq_state; uint32_t vram_mask; uint8_t data_available; int card_type; struct { uint16_t subsys_cntl; uint16_t setup_md; uint8_t advfunc_cntl; uint16_t cur_y, cur_y2; uint16_t cur_x, cur_x2; uint16_t x2, ropmix; uint16_t pat_x, pat_y; int16_t desty_axstp, desty_axstp2; int16_t destx_distp; int16_t maj_axis_pcnt, maj_axis_pcnt2; int16_t err_term, err_term2; uint16_t cmd, cmd2; uint16_t short_stroke; uint32_t pat_bg_color, pat_fg_color; uint32_t bkgd_color; uint32_t frgd_color; uint32_t wrt_mask; uint32_t rd_mask; uint32_t color_cmp; uint8_t bkgd_mix; uint8_t frgd_mix; uint16_t multifunc_cntl; uint16_t multifunc[16]; uint8_t pix_trans[4]; int ssv_state; int16_t cx, cy; int16_t px, py; int16_t sx, sy; int16_t dx, dy; uint32_t src, dest, pattern; int poly_cx, poly_cx2; int poly_cy, poly_cy2; int poly_line_cx; int point_1_updated, point_2_updated; int poly_dx1, poly_dx2; int poly_x; uint32_t dat_buf; int dat_count; int b2e8_pix, temp_cnt; int ssv_len; uint8_t ssv_dir; uint8_t ssv_draw; uint8_t dat_buf_16bit; uint8_t frgd_color_actual[2]; uint8_t bkgd_color_actual[2]; uint8_t wrt_mask_actual[2]; uint8_t rd_mask_actual[2]; uint8_t *pix_trans_ptr; int pix_trans_ptr_cnt; int pix_trans_x_count; int pix_trans_x_count2; int color_16bit_check; int color_16bit_check_rectfill; uint16_t minus, srcminus; /*For non-threaded FIFO*/ int setup_fifo_slot; int draw_fifo_slot; int setup_fifo, setup_fifo2; int draw_fifo, draw_fifo2; } accel; struct { uint32_t nop; uint32_t cntl; uint32_t stretch_filt_const; uint32_t src_dst_step; uint32_t crop; uint32_t src_base, dest_base; uint32_t src, dest; uint32_t srcbase, dstbase; int32_t dda_init_accumulator; int32_t k1, k2; int dm_index; int dither_matrix_idx; int src_step, dst_step; int sx, sx_backup, sy; double cx, dx; double cy, dy; int sx_scale_int, sx_scale_int_backup; double sx_scale; double sx_scale_dec; double sx_scale_inc; double sx_scale_backup; double sx_scale_len; int dither, host_data, scale_down; int input; int len, start; int odf, idf, yuv; atomic_int busy; } videoengine; 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, pri_y, pri_w, pri_h; int sec_x, sec_y, sec_w, sec_h; } streams; fifo_entry_t fifo[FIFO_SIZE]; atomic_int fifo_read_idx, fifo_write_idx; uint8_t fifo_thread_run; thread_t *fifo_thread; event_t *wake_fifo_thread; event_t *fifo_not_full_event; int blitter_busy; uint64_t blitter_time; uint64_t status_time; uint8_t subsys_cntl, subsys_stat; uint32_t hwc_fg_col, hwc_bg_col; int hwc_col_stack_pos; int translate; int enable_8514; int color_16bit; atomic_int busy, force_busy; uint8_t thread_run, serialport; void *i2c, *ddc; int vram; void (*accel_start)(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, void *priv); } s3_t; #define INT_VSY (1 << 0) #define INT_GE_BSY (1 << 1) #define INT_FIFO_OVR (1 << 2) #define INT_FIFO_EMP (1 << 3) #define INT_MASK 0xf #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_updatemapping(s3_t *s3); static void s3_accel_write(uint32_t addr, uint8_t val, void *priv); static void s3_accel_write_w(uint32_t addr, uint16_t val, void *priv); static void s3_accel_write_l(uint32_t addr, uint32_t val, void *priv); static uint8_t s3_accel_read(uint32_t addr, void *priv); static uint16_t s3_accel_read_w(uint32_t addr, void *priv); static uint32_t s3_accel_read_l(uint32_t addr, void *priv); static void s3_out(uint16_t addr, uint8_t val, void *priv); static uint8_t s3_in(uint16_t addr, void *priv); static void s3_accel_out(uint16_t port, uint8_t val, void *priv); static void s3_accel_out_w(uint16_t port, uint16_t val, void *priv); static void s3_accel_out_l(uint16_t port, uint32_t val, void *priv); static uint8_t s3_accel_in(uint16_t port, void *priv); static uint16_t s3_accel_in_w(uint16_t port, void *priv); static uint32_t s3_accel_in_l(uint16_t port, void *priv); static uint8_t s3_pci_read(int func, int addr, void *priv); static void s3_pci_write(int func, int addr, uint8_t val, void *priv); #ifdef ENABLE_S3_LOG int s3_do_log = ENABLE_S3_LOG; static void s3_log(const char *fmt, ...) { va_list ap; if (s3_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else # define s3_log(fmt, ...) #endif /*Remap address for chain-4/doubleword style layout. These will stay for convenience.*/ static __inline uint32_t dword_remap(svga_t *svga, uint32_t in_addr) { if (svga->packed_chain4 || svga->force_old_addr) return in_addr; return ((in_addr << 2) & 0x3fff0) | ((in_addr >> 14) & 0xc) | (in_addr & ~0x3fffc); } static __inline uint32_t dword_remap_w(svga_t *svga, uint32_t in_addr) { if (svga->packed_chain4 || svga->force_old_addr) return in_addr; return ((in_addr << 2) & 0x1fff8) | ((in_addr >> 14) & 0x6) | (in_addr & ~0x1fffe); } static __inline uint32_t dword_remap_l(svga_t *svga, uint32_t in_addr) { if (svga->packed_chain4 || svga->force_old_addr) return in_addr; return ((in_addr << 2) & 0xfffc) | ((in_addr >> 14) & 0x3) | (in_addr & ~0xffff); } static __inline void wake_fifo_thread(s3_t *s3) { thread_set_event(s3->wake_fifo_thread); /*Wake up FIFO thread if moving from idle*/ } static void s3_wait_fifo_idle(s3_t *s3) { while (!FIFO_EMPTY) { wake_fifo_thread(s3); thread_wait_event(s3->fifo_not_full_event, 1); } } static void s3_queue(s3_t *s3, uint32_t addr, uint32_t val, uint32_t type) { fifo_entry_t *fifo = &s3->fifo[s3->fifo_write_idx & FIFO_MASK]; if (FIFO_FULL) { thread_reset_event(s3->fifo_not_full_event); if (FIFO_FULL) { thread_wait_event(s3->fifo_not_full_event, -1); /*Wait for room in ringbuffer*/ } } fifo->val = val; fifo->addr_type = (addr & FIFO_ADDR) | type; s3->fifo_write_idx++; if (FIFO_ENTRIES > 0xe000 || FIFO_ENTRIES < 8) wake_fifo_thread(s3); } static void s3_update_irqs(s3_t *s3) { if (!s3->pci) return; if (s3->subsys_cntl & s3->subsys_stat & INT_MASK) { pci_set_irq(s3->pci_slot, PCI_INTA, &s3->irq_state); } else { pci_clear_irq(s3->pci_slot, PCI_INTA, &s3->irq_state); } } void s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, void *priv); void s3_short_stroke_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_t *s3, uint8_t ssv); static void s3_visionx68_video_engine_op(uint32_t cpu_dat, s3_t *s3); #define WRITE8(addr, var, val) \ switch ((addr) &3) { \ case 0: \ var = (var & 0xffffff00) | (val); \ break; \ case 1: \ var = (var & 0xffff00ff) | ((val) << 8); \ break; \ case 2: \ var = (var & 0xff00ffff) | ((val) << 16); \ break; \ case 3: \ var = (var & 0x00ffffff) | ((val) << 24); \ break; \ } #define READ_PIXTRANS_BYTE_IO(n) \ s3->accel.pix_trans[n] = svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx + n - s3->accel.minus)) & s3->vram_mask]; #define READ_PIXTRANS_BYTE_MM \ temp = svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx)) & s3->vram_mask]; #define READ_PIXTRANS_WORD \ if ((s3->bpp == 0) && !s3->color_16bit) { \ temp = svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx)) & s3->vram_mask]; \ temp |= (svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx + 1)) & s3->vram_mask] << 8); \ } else { \ temp = vram_w[dword_remap_w(svga, (s3->accel.dest + s3->accel.cx - s3->accel.minus)) & (s3->vram_mask >> 1)]; \ } #define READ_PIXTRANS_LONG \ if ((s3->bpp == 0) && !s3->color_16bit) { \ temp = svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx)) & s3->vram_mask]; \ temp |= (svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx + 1)) & s3->vram_mask] << 8); \ temp |= (svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx + 2)) & s3->vram_mask] << 16); \ temp |= (svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx + 3)) & s3->vram_mask] << 24); \ } else { \ temp = vram_w[dword_remap_w(svga, (s3->accel.dest + s3->accel.cx)) & (s3->vram_mask >> 1)]; \ temp |= (vram_w[dword_remap_w(svga, (s3->accel.dest + s3->accel.cx + 2)) & (s3->vram_mask >> 1)] << 16); \ } static int s3_cpu_src(s3_t *s3) { if (!(s3->accel.cmd & 0x100)) return 0; if (s3->chip >= S3_VISION964) return 1; if (s3->accel.cmd & 1) return 1; return 0; } static int s3_cpu_dest(s3_t *s3) { if (!(s3->accel.cmd & 0x100)) return 0; if (s3->chip >= S3_VISION964) return 0; if (s3->accel.cmd & 1) return 0; return 1; } static int s3_enable_fifo(s3_t *s3) { const svga_t *svga = &s3->svga; if ((s3->chip == S3_TRIO32) || (s3->chip == S3_TRIO64) || (s3->chip == S3_TRIO64V) || (s3->chip == S3_TRIO64V2) || (s3->chip == S3_VISION864) || (s3->chip == S3_VISION964) || (s3->chip == S3_VISION968) || (s3->chip == S3_VISION868)) return 1; /* FIFO always enabled on these chips. */ return !!((svga->crtc[0x40] & 0x08) || (s3->accel.advfunc_cntl & 0x40)); } static void s3_accel_out_pixtrans_w(s3_t *s3, uint16_t val) { const svga_t *svga = &s3->svga; if (s3->accel.cmd & 0x100) { s3_log("S3 PIXTRANS_W write: cmd=%03x, pixelcntl=%02x, frgdmix=%02x, bkgdmix=%02x, " "curx=%d, val=%04x.\n", s3->accel.cmd, s3->accel.multifunc[0x0a], s3->accel.frgd_mix, s3->accel.bkgd_mix, s3->accel.cur_x, val); switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) val = (val >> 8) | (val << 8); s3->accel_start(8, 1, val | (val << 16), 0, s3); } else s3->accel_start(1, 1, 0xffffffff, val | (val << 16), s3); } else { if (s3->accel.color_16bit_check_rectfill) { if (s3->accel.color_16bit_check) { if (s3->accel.pix_trans_x_count < s3->accel.pix_trans_ptr_cnt) { s3_log("Word: CPU data CMD=%04x, byte write=%02x, " "cnt=%d, check=%d.\n", s3->accel.cmd, val & 0xff, s3->accel.pix_trans_x_count, s3->accel.color_16bit_check); s3->accel.pix_trans_ptr[s3->accel.pix_trans_x_count] = val & 0xff; s3->accel.pix_trans_ptr[s3->accel.pix_trans_x_count + 1] = val >> 8; s3->accel.pix_trans_x_count += 2; } } break; } s3->accel_start(1, 1, 0xffffffff, val | (val << 16), s3); } break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) val = (val >> 8) | (val << 8); s3->accel_start(16, 1, val | (val << 16), 0, s3); } else s3->accel_start(2, 1, 0xffffffff, val | (val << 16), s3); } else { if (s3->accel.color_16bit_check_rectfill) { if (s3->accel.color_16bit_check) { if (s3->accel.pix_trans_x_count < s3->accel.pix_trans_ptr_cnt) { s3_log("Word: CPU data CMD=%04x, word write=%04x, cnt=%d, check=%d, " "totalptrcnt=%d.\n", s3->accel.cmd, val, s3->accel.pix_trans_x_count, s3->accel.color_16bit_check, s3->accel.pix_trans_ptr_cnt); s3->accel.pix_trans_ptr[s3->accel.pix_trans_x_count] = val & 0xff; s3->accel.pix_trans_ptr[s3->accel.pix_trans_x_count + 1] = val >> 8; s3->accel.pix_trans_x_count += 2; s3->accel.pix_trans_x_count2 = s3->accel.pix_trans_x_count; } } else { if (s3->accel.pix_trans_x_count < s3->accel.pix_trans_ptr_cnt) { s3_log("Word: CPU data CMD=%04x, word write=%04x, cnt=%d, check=%d, " "totalptrcnt=%d.\n", s3->accel.cmd, val, s3->accel.pix_trans_x_count, s3->accel.color_16bit_check, s3->accel.pix_trans_ptr_cnt); s3->accel.pix_trans_ptr[s3->accel.pix_trans_x_count2] = val & 0xff; s3->accel.pix_trans_ptr[s3->accel.pix_trans_x_count2 + 1] = val >> 8; s3->accel.pix_trans_x_count += 2; } if (s3->accel.pix_trans_x_count2 == s3->accel.pix_trans_ptr_cnt) { for (int i = 0; i < s3->accel.pix_trans_ptr_cnt; i += 2) { s3_log("Transferring write count=%d, bytes=%08x.\n", i, s3->accel.pix_trans_ptr[i] | (s3->accel.pix_trans_ptr[i + 1] << 8) | (s3->accel.pix_trans_ptr[i + 2] << 16) | (s3->accel.pix_trans_ptr[i + 3] << 24)); s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans_ptr[i] | (s3->accel.pix_trans_ptr[i + 1] << 8), s3); } s3->accel.pix_trans_x_count2 = 0; s3->accel.color_16bit_check_rectfill = 0; if (s3->accel.pix_trans_ptr != NULL) { free(s3->accel.pix_trans_ptr); s3->accel.pix_trans_ptr = NULL; } } } break; } s3->accel_start(2, 1, 0xffffffff, val | (val << 16), s3); } break; case 0x400: if (svga->crtc[0x53] & 0x08) { if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) val = (val >> 8) | (val << 8); s3->accel_start(32, 1, val | (val << 16), 0, s3); } else s3->accel_start(4, 1, 0xffffffff, val | (val << 16), s3); } else s3->accel_start(4, 1, 0xffffffff, val | (val << 16), s3); } else { if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) val = (val >> 8) | (val << 8); s3->accel_start(16, 1, val | (val << 16), 0, s3); } else s3->accel_start(4, 1, 0xffffffff, val | (val << 16), s3); } else s3->accel_start(4, 1, 0xffffffff, val | (val << 16), s3); } break; case 0x600: if (s3->chip == S3_TRIO32 || s3->chip == S3_VISION968 || s3->chip == S3_VISION868 || s3->chip >= S3_TRIO64V) { if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) val = (val >> 8) | (val << 8); s3->accel_start(8, 1, (val >> 8) & 0xff, 0, s3); s3->accel_start(8, 1, val & 0xff, 0, s3); } } } break; default: break; } } } static void s3_accel_out_pixtrans_l(s3_t *s3, uint32_t val) { if (s3->accel.cmd & 0x100) { switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) val = ((val & 0xff00ff00) >> 8) | ((val & 0x00ff00ff) << 8); s3->accel_start(8, 1, val, 0, s3); s3->accel_start(8, 1, val >> 16, 0, s3); } else { s3->accel_start(1, 1, 0xffffffff, val, s3); s3->accel_start(1, 1, 0xffffffff, val >> 16, s3); } } else { s3->accel_start(1, 1, 0xffffffff, val, s3); s3->accel_start(1, 1, 0xffffffff, val >> 16, s3); } break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) val = ((val & 0xff00ff00) >> 8) | ((val & 0x00ff00ff) << 8); s3->accel_start(16, 1, val, 0, s3); s3->accel_start(16, 1, val >> 16, 0, s3); } else { s3->accel_start(2, 1, 0xffffffff, val, s3); s3->accel_start(2, 1, 0xffffffff, val >> 16, s3); } } else { s3->accel_start(2, 1, 0xffffffff, val, s3); s3->accel_start(2, 1, 0xffffffff, val >> 16, s3); } break; case 0x400: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) val = ((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24); s3->accel_start(32, 1, val, 0, s3); } else s3->accel_start(4, 1, 0xffffffff, val, s3); } else s3->accel_start(4, 1, 0xffffffff, val, s3); break; case 0x600: if (s3->chip == S3_TRIO32 || s3->chip == S3_VISION968 || s3->chip == S3_VISION868 || s3->chip >= S3_TRIO64V) { if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) val = ((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24); s3->accel_start(8, 1, (val >> 24) & 0xff, 0, s3); s3->accel_start(8, 1, (val >> 16) & 0xff, 0, s3); s3->accel_start(8, 1, (val >> 8) & 0xff, 0, s3); s3->accel_start(8, 1, val & 0xff, 0, s3); } } } break; default: break; } } } static void s3_accel_out_fifo(s3_t *s3, uint16_t port, uint8_t val) { const svga_t *svga = &s3->svga; switch (port) { case 0x8148: case 0x82e8: s3->accel.cur_y = (s3->accel.cur_y & 0xf00) | val; s3->accel.poly_cy = s3->accel.cur_y; break; case 0x8149: case 0x82e9: s3->accel.cur_y = (s3->accel.cur_y & 0xff) | ((val & 0x0f) << 8); s3->accel.poly_cy = s3->accel.cur_y; break; case 0x814a: case 0x82ea: s3->accel.cur_y2 = (s3->accel.cur_y2 & 0xf00) | val; s3->accel.poly_cy2 = s3->accel.cur_y2; break; case 0x814b: case 0x82eb: s3->accel.cur_y2 = (s3->accel.cur_y2 & 0xff) | ((val & 0x0f) << 8); s3->accel.poly_cy2 = s3->accel.cur_y2; break; case 0x8548: case 0x86e8: s3->accel.cur_x = (s3->accel.cur_x & 0xf00) | val; s3->accel.poly_cx = s3->accel.cur_x << 20; s3->accel.poly_x = s3->accel.poly_cx >> 20; break; case 0x8549: case 0x86e9: s3->accel.cur_x = (s3->accel.cur_x & 0xff) | ((val & 0x0f) << 8); s3->accel.poly_cx = s3->accel.poly_x = s3->accel.cur_x << 20; s3->accel.poly_x = s3->accel.poly_cx >> 20; break; case 0x854a: case 0x86ea: s3->accel.cur_x2 = (s3->accel.cur_x2 & 0xf00) | val; s3->accel.poly_cx2 = s3->accel.cur_x2 << 20; break; case 0x854b: case 0x86eb: s3->accel.cur_x2 = (s3->accel.cur_x2 & 0xff) | ((val & 0x0f) << 8); s3->accel.poly_cx2 = s3->accel.cur_x2 << 20; break; case 0xcae8: case 0x8948: case 0x8ae8: s3->accel.desty_axstp = (s3->accel.desty_axstp & 0x3f00) | val; s3->accel.point_1_updated = 1; break; case 0xcae9: case 0x8949: case 0x8ae9: s3->accel.desty_axstp = (s3->accel.desty_axstp & 0xff) | ((val & 0x3f) << 8); if (val & 0x20) s3->accel.desty_axstp |= ~0x3fff; s3->accel.point_1_updated = 1; break; case 0x894a: case 0x8aea: s3->accel.desty_axstp2 = (s3->accel.desty_axstp2 & 0x3f00) | val; s3->accel.point_2_updated = 1; break; case 0x849b: case 0x8aeb: s3->accel.desty_axstp2 = (s3->accel.desty_axstp2 & 0xff) | ((val & 0x3f) << 8); if (val & 0x20) s3->accel.desty_axstp2 |= ~0x3fff; s3->accel.point_2_updated = 1; break; case 0x8d48: case 0x8ee8: s3->accel.destx_distp = (s3->accel.destx_distp & 0x3f00) | val; s3->accel.point_1_updated = 1; break; case 0x8d49: case 0x8ee9: s3->accel.destx_distp = (s3->accel.destx_distp & 0xff) | ((val & 0x3f) << 8); if (val & 0x20) s3->accel.destx_distp |= ~0x3fff; s3->accel.point_1_updated = 1; break; case 0x8d4a: case 0x8eea: s3->accel.x2 = (s3->accel.x2 & 0xf00) | val; s3->accel.point_2_updated = 1; break; case 0x8d4b: case 0x8eeb: s3->accel.x2 = (s3->accel.x2 & 0xff) | ((val & 0x0f) << 8); s3->accel.point_2_updated = 1; break; case 0x9148: case 0x92e8: s3->accel.err_term = (s3->accel.err_term & 0x3f00) | val; break; case 0x9149: case 0x92e9: s3->accel.err_term = (s3->accel.err_term & 0xff) | ((val & 0x3f) << 8); if (val & 0x20) s3->accel.err_term |= ~0x1fff; break; case 0x914a: case 0x92ea: s3->accel.err_term2 = (s3->accel.err_term2 & 0x3f00) | val; break; case 0x914b: case 0x92eb: s3->accel.err_term2 = (s3->accel.err_term2 & 0xff) | ((val & 0x3f) << 8); if (val & 0x20) s3->accel.err_term2 |= ~0x1fff; break; case 0x9548: case 0x96e8: s3->accel.maj_axis_pcnt = (s3->accel.maj_axis_pcnt & 0xf00) | val; break; case 0x9459: case 0x96e9: s3->accel.maj_axis_pcnt = (s3->accel.maj_axis_pcnt & 0xff) | ((val & 0x0f) << 8); break; case 0x954a: case 0x96ea: s3->accel.maj_axis_pcnt2 = (s3->accel.maj_axis_pcnt2 & 0xf00) | val; break; case 0x954b: case 0x96eb: s3->accel.maj_axis_pcnt2 = (s3->accel.maj_axis_pcnt2 & 0xff) | ((val & 0x0f) << 8); break; case 0x9948: case 0x9ae8: s3->accel.cmd = (s3->accel.cmd & 0xff00) | val; s3->data_available = 0; s3->accel.b2e8_pix = 0; break; case 0x9949: case 0x9ae9: s3->accel.cmd = (s3->accel.cmd & 0xff) | (val << 8); s3->accel.ssv_state = 0; s3->accel_start(-1, 0, 0xffffffff, 0, s3); break; case 0x994a: case 0x9aea: s3->accel.cmd2 = (s3->accel.cmd2 & 0xff00) | val; break; case 0x994b: case 0x9aeb: s3->accel.cmd2 = (s3->accel.cmd2 & 0xff) | (val << 8); break; case 0x9d48: case 0x9ee8: s3->accel.short_stroke = (s3->accel.short_stroke & 0xff00) | val; break; case 0x9d49: case 0x9ee9: s3->accel.short_stroke = (s3->accel.short_stroke & 0xff) | (val << 8); s3->accel.ssv_state = 1; s3->accel.cx = s3->accel.cur_x & 0xfff; s3->accel.cy = s3->accel.cur_y & 0xfff; if (s3->accel.cmd & 0x1000) { s3_short_stroke_start(-1, 0, 0xffffffff, 0, s3, s3->accel.short_stroke & 0xff); s3_short_stroke_start(-1, 0, 0xffffffff, 0, s3, s3->accel.short_stroke >> 8); } else { s3_short_stroke_start(-1, 0, 0xffffffff, 0, s3, s3->accel.short_stroke >> 8); s3_short_stroke_start(-1, 0, 0xffffffff, 0, s3, s3->accel.short_stroke & 0xff); } break; case 0xa148: case 0xa2e8: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.bkgd_color = (s3->accel.bkgd_color & ~0x00ff0000) | (val << 16); else s3->accel.bkgd_color = (s3->accel.bkgd_color & ~0x000000ff) | val; break; case 0xa149: case 0xa2e9: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.bkgd_color = (s3->accel.bkgd_color & ~0xff000000) | (val << 24); else s3->accel.bkgd_color = (s3->accel.bkgd_color & ~0x0000ff00) | (val << 8); if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; if (s3->accel.color_16bit_check) s3->accel.bkgd_color_actual[1] = s3->accel.bkgd_color & 0xff; else s3->accel.bkgd_color_actual[0] = s3->accel.bkgd_color & 0xff; break; case 0xa14a: case 0xa2ea: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.bkgd_color = (s3->accel.bkgd_color & ~0x00ff0000) | (val << 16); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.bkgd_color = (s3->accel.bkgd_color & ~0x00ff0000) | (val << 16); else s3->accel.bkgd_color = (s3->accel.bkgd_color & ~0x000000ff) | val; } break; case 0xa14b: case 0xa2eb: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.bkgd_color = (s3->accel.bkgd_color & ~0xff000000) | (val << 24); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.bkgd_color = (s3->accel.bkgd_color & ~0xff000000) | (val << 24); else s3->accel.bkgd_color = (s3->accel.bkgd_color & ~0x0000ff00) | (val << 8); s3->accel.multifunc[0xe] ^= 0x10; } break; case 0xa548: case 0xa6e8: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.frgd_color = (s3->accel.frgd_color & ~0x00ff0000) | (val << 16); else s3->accel.frgd_color = (s3->accel.frgd_color & ~0x000000ff) | val; break; case 0xa549: case 0xa6e9: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.frgd_color = (s3->accel.frgd_color & ~0xff000000) | (val << 24); else s3->accel.frgd_color = (s3->accel.frgd_color & ~0x0000ff00) | (val << 8); if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; if (s3->accel.color_16bit_check) s3->accel.frgd_color_actual[1] = s3->accel.frgd_color & 0xff; else s3->accel.frgd_color_actual[0] = s3->accel.frgd_color & 0xff; break; case 0xa54a: case 0xa6ea: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.frgd_color = (s3->accel.frgd_color & ~0x00ff0000) | (val << 16); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.frgd_color = (s3->accel.frgd_color & ~0x00ff0000) | (val << 16); else s3->accel.frgd_color = (s3->accel.frgd_color & ~0x000000ff) | val; } break; case 0xa54b: case 0xa6eb: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.frgd_color = (s3->accel.frgd_color & ~0xff000000) | (val << 24); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.frgd_color = (s3->accel.frgd_color & ~0xff000000) | (val << 24); else s3->accel.frgd_color = (s3->accel.frgd_color & ~0x0000ff00) | (val << 8); s3->accel.multifunc[0xe] ^= 0x10; } break; case 0xa948: case 0xaae8: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.wrt_mask = (s3->accel.wrt_mask & ~0x00ff0000) | (val << 16); else s3->accel.wrt_mask = (s3->accel.wrt_mask & ~0x000000ff) | val; break; case 0xa949: case 0xaae9: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.wrt_mask = (s3->accel.wrt_mask & ~0xff000000) | (val << 24); else s3->accel.wrt_mask = (s3->accel.wrt_mask & ~0x0000ff00) | (val << 8); if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; if (s3->accel.color_16bit_check) s3->accel.wrt_mask_actual[1] = s3->accel.wrt_mask & 0xff; else s3->accel.wrt_mask_actual[0] = s3->accel.wrt_mask & 0xff; break; case 0xa94a: case 0xaaea: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.wrt_mask = (s3->accel.wrt_mask & ~0x00ff0000) | (val << 16); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.wrt_mask = (s3->accel.wrt_mask & ~0x00ff0000) | (val << 16); else s3->accel.wrt_mask = (s3->accel.wrt_mask & ~0x000000ff) | val; } break; case 0xa94b: case 0xaaeb: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.wrt_mask = (s3->accel.wrt_mask & ~0xff000000) | (val << 24); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.wrt_mask = (s3->accel.wrt_mask & ~0xff000000) | (val << 24); else s3->accel.wrt_mask = (s3->accel.wrt_mask & ~0x0000ff00) | (val << 8); s3->accel.multifunc[0xe] ^= 0x10; } break; case 0xad48: case 0xaee8: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.rd_mask = (s3->accel.rd_mask & ~0x00ff0000) | (val << 16); else s3->accel.rd_mask = (s3->accel.rd_mask & ~0x000000ff) | val; break; case 0xad49: case 0xaee9: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.rd_mask = (s3->accel.rd_mask & ~0xff000000) | (val << 24); else s3->accel.rd_mask = (s3->accel.rd_mask & ~0x0000ff00) | (val << 8); if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; break; case 0xad4a: case 0xaeea: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.rd_mask = (s3->accel.rd_mask & ~0x00ff0000) | (val << 16); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.rd_mask = (s3->accel.rd_mask & ~0x00ff0000) | (val << 16); else s3->accel.rd_mask = (s3->accel.rd_mask & ~0x000000ff) | val; } break; case 0xad4b: case 0xaeeb: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.rd_mask = (s3->accel.rd_mask & ~0xff000000) | (val << 24); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.rd_mask = (s3->accel.rd_mask & ~0xff000000) | (val << 24); else s3->accel.rd_mask = (s3->accel.rd_mask & ~0x0000ff00) | (val << 8); s3->accel.multifunc[0xe] ^= 0x10; } break; case 0xb148: case 0xb2e8: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.color_cmp = (s3->accel.color_cmp & ~0x00ff0000) | (val << 16); else s3->accel.color_cmp = (s3->accel.color_cmp & ~0x000000ff) | val; break; case 0xb149: case 0xb2e9: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.color_cmp = (s3->accel.color_cmp & ~0xff000000) | (val << 24); else s3->accel.color_cmp = (s3->accel.color_cmp & ~0x0000ff00) | (val << 8); if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; break; case 0xb14a: case 0xb2ea: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.color_cmp = (s3->accel.color_cmp & ~0x00ff0000) | (val << 16); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.color_cmp = (s3->accel.color_cmp & ~0x00ff0000) | (val << 16); else s3->accel.color_cmp = (s3->accel.color_cmp & ~0x000000ff) | val; } break; case 0xb14b: case 0xb2eb: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.color_cmp = (s3->accel.color_cmp & ~0xff000000) | (val << 24); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.color_cmp = (s3->accel.color_cmp & ~0xff000000) | (val << 24); else s3->accel.color_cmp = (s3->accel.color_cmp & ~0x0000ff00) | (val << 8); s3->accel.multifunc[0xe] ^= 0x10; } break; case 0xb548: case 0xb6e8: s3->accel.bkgd_mix = val; break; case 0xb948: case 0xbae8: s3->accel.frgd_mix = val; break; case 0xbd48: case 0xbee8: s3->accel.multifunc_cntl = (s3->accel.multifunc_cntl & 0xff00) | val; break; case 0xbd49: case 0xbee9: s3->accel.multifunc_cntl = (s3->accel.multifunc_cntl & 0xff) | (val << 8); s3->accel.multifunc[s3->accel.multifunc_cntl >> 12] = s3->accel.multifunc_cntl & 0xfff; break; case 0xd148: case 0xd2e8: s3->accel.ropmix = (s3->accel.ropmix & 0xff00) | val; break; case 0xd149: case 0xd2e9: s3->accel.ropmix = (s3->accel.ropmix & 0x00ff) | (val << 8); break; case 0xe548: case 0xe6e8: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.pat_bg_color = (s3->accel.pat_bg_color & ~0x00ff0000) | (val << 16); else s3->accel.pat_bg_color = (s3->accel.pat_bg_color & ~0x000000ff) | val; break; case 0xe549: case 0xe6e9: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.pat_bg_color = (s3->accel.pat_bg_color & ~0xff000000) | (val << 24); else s3->accel.pat_bg_color = (s3->accel.pat_bg_color & ~0x0000ff00) | (val << 8); if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; break; case 0xe54a: case 0xe6ea: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.pat_bg_color = (s3->accel.pat_bg_color & ~0x00ff0000) | (val << 16); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.pat_bg_color = (s3->accel.pat_bg_color & ~0x00ff0000) | (val << 16); else s3->accel.pat_bg_color = (s3->accel.pat_bg_color & ~0x000000ff) | val; } break; case 0xe54b: case 0xe6eb: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.pat_bg_color = (s3->accel.pat_bg_color & ~0xff000000) | (val << 24); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.pat_bg_color = (s3->accel.pat_bg_color & ~0xff000000) | (val << 24); else s3->accel.pat_bg_color = (s3->accel.pat_bg_color & ~0x0000ff00) | (val << 8); s3->accel.multifunc[0xe] ^= 0x10; } break; case 0xe948: case 0xeae8: s3->accel.pat_y = (s3->accel.pat_y & 0xf00) | val; break; case 0xe949: case 0xeae9: s3->accel.pat_y = (s3->accel.pat_y & 0xff) | ((val & 0x1f) << 8); break; case 0xe94a: case 0xeaea: s3->accel.pat_x = (s3->accel.pat_x & 0xf00) | val; break; case 0xe94b: case 0xeaeb: s3->accel.pat_x = (s3->accel.pat_x & 0xff) | ((val & 0x1f) << 8); break; case 0xed48: case 0xeee8: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.pat_fg_color = (s3->accel.pat_fg_color & ~0x00ff0000) | (val << 16); else s3->accel.pat_fg_color = (s3->accel.pat_fg_color & ~0x000000ff) | val; break; case 0xed49: case 0xeee9: if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) s3->accel.pat_fg_color = (s3->accel.pat_fg_color & ~0xff000000) | (val << 24); else s3->accel.pat_fg_color = (s3->accel.pat_fg_color & ~0x0000ff00) | (val << 8); if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; break; case 0xed4a: case 0xeeea: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.pat_fg_color = (s3->accel.pat_fg_color & ~0x00ff0000) | (val << 16); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.pat_fg_color = (s3->accel.pat_fg_color & ~0x00ff0000) | (val << 16); else s3->accel.pat_fg_color = (s3->accel.pat_fg_color & ~0x000000ff) | val; } break; case 0xed4b: case 0xeeeb: if (s3->accel.multifunc[0xe] & 0x200) s3->accel.pat_fg_color = (s3->accel.pat_fg_color & ~0xff000000) | (val << 24); else if (s3->bpp == 3) { if (s3->accel.multifunc[0xe] & 0x10) s3->accel.pat_fg_color = (s3->accel.pat_fg_color & ~0xff000000) | (val << 24); else s3->accel.pat_fg_color = (s3->accel.pat_fg_color & ~0x0000ff00) | (val << 8); s3->accel.multifunc[0xe] ^= 0x10; } break; case 0xe148: case 0xe2e8: s3->accel.b2e8_pix = 0; if (s3_cpu_dest(s3)) break; s3->accel.pix_trans[0] = val; if (s3->accel.cmd & 0x100) { switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(8, 1, s3->accel.pix_trans[0], 0, s3); else s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0], s3); } else { if (s3->accel.color_16bit_check_rectfill) { if (s3->accel.color_16bit_check) { if (s3->accel.pix_trans_x_count < s3->accel.pix_trans_ptr_cnt) { s3_log("Byte: CPU data CMD=%04x, byte write=%02x, cnt=%d, " "check=%d.\n", s3->accel.cmd, val, s3->accel.pix_trans_x_count, s3->accel.color_16bit_check); s3->accel.pix_trans_ptr[s3->accel.pix_trans_x_count] = val; s3->accel.pix_trans_x_count++; s3->accel.pix_trans_x_count2 = s3->accel.pix_trans_x_count; } } else { if (s3->accel.pix_trans_x_count2 < s3->accel.pix_trans_ptr_cnt) { s3_log("Byte: CPU data CMD=%04x, byte write=%02x, cnt=%d, " "check=%d.\n", s3->accel.cmd, val, s3->accel.pix_trans_x_count2, s3->accel.color_16bit_check); s3->accel.pix_trans_ptr[s3->accel.pix_trans_x_count2] = val; s3->accel.pix_trans_x_count2++; } s3_log("WriteCNT=%d, TotalCNT=%d.\n", s3->accel.pix_trans_x_count2, s3->accel.pix_trans_ptr_cnt); if (s3->accel.pix_trans_x_count2 == s3->accel.pix_trans_ptr_cnt) { for (int i = 0; i < s3->accel.pix_trans_ptr_cnt; i += 2) { s3_log("Transferring write count=%d, bytes=%04x.\n", i, s3->accel.pix_trans_ptr[i] | (s3->accel.pix_trans_ptr[i + 1] << 8)); s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans_ptr[i] | (s3->accel.pix_trans_ptr[i + 1] << 8), s3); } s3->accel.pix_trans_x_count2 = 0; s3->accel.color_16bit_check_rectfill = 0; if (s3->accel.pix_trans_ptr != NULL) { free(s3->accel.pix_trans_ptr); s3->accel.pix_trans_ptr = NULL; } } } break; } s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0], s3); } break; default: break; } } break; case 0xe149: case 0xe2e9: s3->accel.b2e8_pix = 0; if (s3_cpu_dest(s3)) break; s3->accel.pix_trans[1] = val; if (s3->accel.cmd & 0x100) { s3_log("S3 PIXTRANS_B write (E2E9): cmd=%03x, pixelcntl=%02x, frgdmix=%02x, " "bkgdmix=%02x, curx=%d, val=%04x.\n", s3->accel.cmd, s3->accel.multifunc[0x0a], s3->accel.frgd_mix, s3->accel.bkgd_mix, s3->accel.cur_x, val); switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(8, 1, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), 0, s3); else s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); } else s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) s3->accel_start(16, 1, s3->accel.pix_trans[1] | (s3->accel.pix_trans[0] << 8), 0, s3); else s3->accel_start(16, 1, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), 0, s3); } else { if (s3->accel.cmd & 0x1000) s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[1] | (s3->accel.pix_trans[0] << 8), s3); else s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); } } else { if (s3->accel.cmd & 0x1000) s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[1] | (s3->accel.pix_trans[0] << 8), s3); else s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); } break; case 0x400: if (svga->crtc[0x53] & 0x08) { if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(32, 1, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), 0, s3); else s3->accel_start(4, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); } else s3->accel_start(4, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); } break; case 0x600: if (s3->chip == S3_TRIO32 || s3->chip == S3_VISION968 || s3->chip == S3_VISION868 || s3->chip >= S3_TRIO64V) { if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { s3->accel_start(8, 1, s3->accel.pix_trans[1], 0, s3); s3->accel_start(8, 1, s3->accel.pix_trans[0], 0, s3); } } } break; default: break; } } break; case 0xe14a: case 0xe2ea: if (s3_cpu_dest(s3)) break; s3->accel.pix_trans[2] = val; break; case 0xe14b: case 0xe2eb: if (s3_cpu_dest(s3)) break; s3->accel.pix_trans[3] = val; if (s3->accel.cmd & 0x100) { switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(8, 1, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), 0, s3); else s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), s3); } else s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), s3); break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) s3->accel_start(16, 1, s3->accel.pix_trans[3] | (s3->accel.pix_trans[2] << 8) | (s3->accel.pix_trans[1] << 16) | (s3->accel.pix_trans[0] << 24), 0, s3); else s3->accel_start(16, 1, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), 0, s3); } else { if (s3->accel.cmd & 0x1000) s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[3] | (s3->accel.pix_trans[2] << 8) | (s3->accel.pix_trans[1] << 16) | (s3->accel.pix_trans[0] << 24), s3); else s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), s3); } } else { if (s3->accel.cmd & 0x1000) s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[3] | (s3->accel.pix_trans[2] << 8) | (s3->accel.pix_trans[1] << 16) | (s3->accel.pix_trans[0] << 24), s3); else s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), s3); } break; case 0x400: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(32, 1, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), 0, s3); else s3->accel_start(4, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), s3); } else s3->accel_start(4, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), s3); break; case 0x600: if (s3->chip == S3_TRIO32 || s3->chip == S3_VISION968 || s3->chip == S3_VISION868 || s3->chip >= S3_TRIO64V) { if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { s3->accel_start(8, 1, s3->accel.pix_trans[3], 0, s3); s3->accel_start(8, 1, s3->accel.pix_trans[2], 0, s3); s3->accel_start(8, 1, s3->accel.pix_trans[1], 0, s3); s3->accel_start(8, 1, s3->accel.pix_trans[0], 0, s3); } } } break; default: break; } } break; default: break; } } static void s3_accel_out_fifo_w(s3_t *s3, uint16_t port, uint16_t val) { if (port != 0x9ee8 && port != 0x9d48) { if (port == 0xb2e8 || port == 0xb148) { s3->accel.b2e8_pix = 1; } else { s3->accel.b2e8_pix = 0; } s3_accel_out_pixtrans_w(s3, val); } else { s3->accel.short_stroke = val; s3->accel.ssv_state = 1; s3->accel.cx = s3->accel.cur_x & 0xfff; s3->accel.cy = s3->accel.cur_y & 0xfff; if (s3->accel.cmd & 0x1000) { s3_short_stroke_start(-1, 0, 0xffffffff, 0, s3, s3->accel.short_stroke & 0xff); s3_short_stroke_start(-1, 0, 0xffffffff, 0, s3, s3->accel.short_stroke >> 8); } else { s3_short_stroke_start(-1, 0, 0xffffffff, 0, s3, s3->accel.short_stroke >> 8); s3_short_stroke_start(-1, 0, 0xffffffff, 0, s3, s3->accel.short_stroke & 0xff); } } } static void s3_accel_out_fifo_l(s3_t *s3, uint16_t port, uint32_t val) { if (port == 0xb2e8 || port == 0xb148) s3->accel.b2e8_pix = 1; else s3->accel.b2e8_pix = 0; s3_accel_out_pixtrans_l(s3, val); } static void s3_accel_write_fifo(s3_t *s3, uint32_t addr, uint8_t val) { const svga_t *svga = &s3->svga; if (s3->packed_mmio) { int addr_lo = addr & 1; if (svga->crtc[0x53] & 0x08) { if ((addr >= 0x08000) && (addr <= 0x0803f)) s3_pci_write(0, addr & 0xff, val, s3); } switch (addr & 0x1fffe) { case 0x8100: addr = 0x82e8; break; /*ALT_CURXY*/ case 0x8102: addr = 0x86e8; break; case 0x8104: addr = 0x82ea; break; /*ALT_CURXY2*/ case 0x8106: addr = 0x86ea; break; case 0x8108: addr = 0x8ae8; break; /*ALT_STEP*/ case 0x810a: addr = 0x8ee8; break; case 0x810c: addr = 0x8aea; break; /*ALT_STEP2*/ case 0x810e: addr = 0x8eea; break; case 0x8110: addr = 0x92e8; break; /*ALT_ERR*/ case 0x8112: addr = 0x92ee; break; case 0x8118: addr = 0x9ae8; break; /*ALT_CMD*/ case 0x811a: addr = 0x9aea; break; case 0x811c: addr = 0x9ee8; break; case 0x8120: case 0x8122: /*BKGD_COLOR*/ WRITE8(addr, s3->accel.bkgd_color, val); return; case 0x8124: case 0x8126: /*FRGD_COLOR*/ WRITE8(addr, s3->accel.frgd_color, val); return; case 0x8128: case 0x812a: /*WRT_MASK*/ WRITE8(addr, s3->accel.wrt_mask, val); return; case 0x812c: case 0x812e: /*RD_MASK*/ WRITE8(addr, s3->accel.rd_mask, val); return; case 0x8130: case 0x8132: /*COLOR_CMP*/ WRITE8(addr, s3->accel.color_cmp, val); return; case 0x8134: addr = 0xb6e8; break; /*ALT_MIX*/ case 0x8136: addr = 0xbae8; break; case 0x8138: /*SCISSORS_T*/ WRITE8(addr & 1, s3->accel.multifunc[1], val); return; case 0x813a: /*SCISSORS_L*/ WRITE8(addr & 1, s3->accel.multifunc[2], val); return; case 0x813c: /*SCISSORS_B*/ WRITE8(addr & 1, s3->accel.multifunc[3], val); return; case 0x813e: /*SCISSORS_R*/ WRITE8(addr & 1, s3->accel.multifunc[4], val); return; case 0x8140: /*PIX_CNTL*/ WRITE8(addr & 1, s3->accel.multifunc[0xa], val); return; case 0x8142: /*MULT_MISC2*/ WRITE8(addr & 1, s3->accel.multifunc[0xd], val); return; case 0x8144: /*MULT_MISC*/ WRITE8(addr & 1, s3->accel.multifunc[0xe], val); return; case 0x8146: /*READ_SEL*/ WRITE8(addr & 1, s3->accel.multifunc[0xf], val); return; case 0x8148: /*ALT_PCNT*/ WRITE8(addr & 1, s3->accel.multifunc[0], val); return; case 0x814a: addr = 0x96e8; break; case 0x814c: addr = 0x96ea; break; case 0x8150: addr = 0xd2e8; break; case 0x8154: addr = 0x8ee8; break; case 0x8156: addr = 0x96e8; break; case 0x8164: case 0x8166: WRITE8(addr, s3->accel.pat_bg_color, val); return; case 0x8168: addr = 0xeae8; break; case 0x816a: addr = 0xeaea; break; case 0x816c: case 0x816e: WRITE8(addr, s3->accel.pat_fg_color, val); return; default: break; } addr |= addr_lo; } if (svga->crtc[0x53] & 0x08) { if ((addr & 0x1ffff) < 0x8000) { if (s3->accel.cmd & 0x100) { if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(8, 1, val | (val << 8) | (val << 16) | (val << 24), 0, s3); else s3->accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); } else s3->accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); } } else { switch (addr & 0x1ffff) { case 0x83b0: case 0x83b1: case 0x83b2: case 0x83b3: case 0x83b4: case 0x83b5: case 0x83b6: case 0x83b7: case 0x83b8: case 0x83b9: case 0x83ba: case 0x83bb: case 0x83bc: case 0x83bd: case 0x83be: case 0x83bf: case 0x83c0: case 0x83c1: case 0x83c2: case 0x83c3: case 0x83c4: case 0x83c5: case 0x83c6: case 0x83c7: case 0x83c8: case 0x83c9: case 0x83ca: case 0x83cb: case 0x83cc: case 0x83cd: case 0x83ce: case 0x83cf: case 0x83d0: case 0x83d1: case 0x83d2: case 0x83d3: case 0x83d4: case 0x83d5: case 0x83d6: case 0x83d7: case 0x83d8: case 0x83d9: case 0x83da: case 0x83db: case 0x83dc: case 0x83dd: case 0x83de: case 0x83df: s3_out(addr & 0x3ff, val, s3); break; case 0x8504: s3->subsys_stat &= ~val; s3_update_irqs(s3); break; case 0x8505: s3->subsys_cntl = val; s3_update_irqs(s3); break; case 0x850c: s3->accel.advfunc_cntl = val; s3_updatemapping(s3); break; case 0xff20: s3->serialport = val; i2c_gpio_set(s3->i2c, !!(val & SERIAL_PORT_SCW), !!(val & SERIAL_PORT_SDW)); break; default: s3_accel_out_fifo(s3, addr & 0xffff, val); break; } } } else { if (addr & 0x8000) { if ((addr == 0xe2e8) || (addr == 0xe2e9) || (addr == 0xe2ea) || (addr == 0xe2eb)) { if ((s3->chip == S3_86C801) || (s3->chip == S3_86C805)) goto mmio_byte_write; else s3_accel_out_fifo(s3, addr & 0xffff, val); } else s3_accel_out_fifo(s3, addr & 0xffff, val); } else { mmio_byte_write: if (s3->accel.cmd & 0x100) { if ((s3->accel.cmd & 0x600) == 0x200) { if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(16, 1, val | (val << 8) | (val << 16) | (val << 24), 0, s3); else s3->accel_start(2, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); } else s3->accel_start(2, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); } else { if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(8, 1, val | (val << 8) | (val << 16) | (val << 24), 0, s3); else s3->accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); } else s3->accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); } } } } } static void s3_accel_write_fifo_w(s3_t *s3, uint32_t addr, uint16_t val) { const svga_t *svga = &s3->svga; if (svga->crtc[0x53] & 0x08) { if ((addr & 0x1fffe) < 0x8000) { s3_accel_out_pixtrans_w(s3, val); } else { switch (addr & 0x1fffe) { default: case 0x83d4: s3_accel_write_fifo(s3, addr, val); s3_accel_write_fifo(s3, addr + 1, val >> 8); break; case 0xff20: s3_accel_write_fifo(s3, addr, val); break; case 0x811c: s3_accel_out_fifo_w(s3, 0x9ee8, val); break; } } } else { if (addr & 0x8000) { if (addr == 0x811c) s3_accel_out_fifo_w(s3, 0x9ee8, val); else { if ((addr == 0xe2e8) || (addr == 0xe2ea)) { if ((s3->chip == S3_86C801) || (s3->chip == S3_86C805) || (s3->chip == S3_86C928) || (s3->chip == S3_86C928PCI)) s3_accel_out_pixtrans_w(s3, val); else { s3_accel_write_fifo(s3, addr, val); s3_accel_write_fifo(s3, addr + 1, val >> 8); } } else { s3_accel_write_fifo(s3, addr, val); s3_accel_write_fifo(s3, addr + 1, val >> 8); } } } else { s3_accel_out_pixtrans_w(s3, val); } } } static void s3_accel_write_fifo_l(s3_t *s3, uint32_t addr, uint32_t val) { svga_t *svga = &s3->svga; if (svga->crtc[0x53] & 0x08) { if ((addr & 0x1fffc) < 0x8000 || ((addr & 0x1fffc) >= 0x10000 && (addr & 0x1fffc) < 0x18000)) { if ((addr & 0x1fffc) >= 0x10000 && (addr & 0x1fffc) < 0x18000) { s3_visionx68_video_engine_op(val, s3); } else if ((addr & 0x1fffc) < 0x8000) { s3_accel_out_pixtrans_l(s3, val); } } else { switch (addr & 0x1fffc) { case 0x8180: s3->streams.pri_ctrl = val; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x8184: s3->streams.chroma_ctrl = val; break; case 0x8190: s3->streams.sec_ctrl = val; s3->streams.dda_horiz_accumulator = val & 0xfff; if (val & (1 << 11)) s3->streams.dda_horiz_accumulator |= 0xfffff800; s3->streams.sdif = (val >> 24) & 7; break; case 0x8194: s3->streams.chroma_upper_bound = val; break; case 0x8198: s3->streams.sec_filter = val; s3->streams.k1_horiz_scale = val & 0x7ff; if (val & (1 << 10)) s3->streams.k1_horiz_scale |= 0xfffff800; s3->streams.k2_horiz_scale = (val >> 16) & 0x7ff; if ((val >> 16) & (1 << 10)) s3->streams.k2_horiz_scale |= 0xfffff800; break; case 0x81a0: s3->streams.blend_ctrl = val; break; case 0x81c0: s3->streams.pri_fb0 = val & 0x3fffff; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x81c4: s3->streams.pri_fb1 = val & 0x3fffff; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x81c8: s3->streams.pri_stride = val & 0xfff; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x81cc: s3->streams.buffer_ctrl = val; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x81d0: s3->streams.sec_fb0 = val; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x81d4: s3->streams.sec_fb1 = val; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x81d8: s3->streams.sec_stride = val; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x81dc: s3->streams.overlay_ctrl = val; break; case 0x81e0: s3->streams.k1_vert_scale = val & 0x7ff; if (val & (1 << 10)) s3->streams.k1_vert_scale |= 0xfffff800; break; case 0x81e4: s3->streams.k2_vert_scale = val & 0x7ff; if (val & (1 << 10)) s3->streams.k2_vert_scale |= 0xfffff800; break; case 0x81e8: s3->streams.dda_vert_accumulator = val & 0xfff; if (val & (1 << 11)) s3->streams.dda_vert_accumulator |= 0xfffff800; break; case 0x81ec: s3->streams.fifo_ctrl = val; break; case 0x81f0: s3->streams.pri_start = val; s3->streams.pri_x = (val >> 16) & 0x7ff; s3->streams.pri_y = val & 0x7ff; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x81f4: s3->streams.pri_size = val; s3->streams.pri_w = (val >> 16) & 0x7ff; s3->streams.pri_h = val & 0x7ff; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x81f8: s3->streams.sec_start = val; s3->streams.sec_x = (val >> 16) & 0x7ff; s3->streams.sec_y = val & 0x7ff; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x81fc: s3->streams.sec_size = val; s3->streams.sec_w = (val >> 16) & 0x7ff; s3->streams.sec_h = val & 0x7ff; svga_recalctimings(svga); svga->fullchange = svga->monitor->mon_changeframecount; break; case 0x8504: s3->subsys_stat &= ~(val & 0xff); s3->subsys_cntl = (val >> 8); s3_update_irqs(s3); break; case 0x850c: s3->accel.advfunc_cntl = val & 0xff; s3_updatemapping(s3); break; case 0xff20: s3_accel_write_fifo(s3, addr, val); break; case 0x18080: s3->videoengine.nop = 1; break; case 0x18088: s3->videoengine.cntl = val; s3->videoengine.dda_init_accumulator = val & 0xfff; s3->videoengine.odf = (val >> 16) & 7; s3->videoengine.yuv = !!(val & (1 << 19)); s3->videoengine.idf = (val >> 20) & 7; s3->videoengine.dither = !!(val & (1 << 29)); s3->videoengine.dm_index = (val >> 23) & 7; break; case 0x1808c: s3->videoengine.stretch_filt_const = val; s3->videoengine.k2 = val & 0x7ff; s3->videoengine.k1 = (val >> 16) & 0x7ff; s3->videoengine.host_data = !!(val & (1 << 30)); s3->videoengine.scale_down = !!(val & (1 << 31)); break; case 0x18090: s3->videoengine.src_dst_step = val; s3->videoengine.dst_step = val & 0x1fff; s3->videoengine.src_step = (val >> 16) & 0x1fff; break; case 0x18094: s3->videoengine.crop = val; s3->videoengine.len = val & 0xfff; s3->videoengine.start = (val >> 16) & 0xfff; s3->videoengine.input = 1; break; case 0x18098: s3->videoengine.src_base = val & 0xffffff; break; case 0x1809c: s3->videoengine.dest_base = val & 0xffffff; break; default: s3_accel_write_fifo(s3, addr, val); s3_accel_write_fifo(s3, addr + 1, val >> 8); s3_accel_write_fifo(s3, addr + 2, val >> 16); s3_accel_write_fifo(s3, addr + 3, val >> 24); break; } } } else { if (addr & 0x8000) { if (addr == 0xe2e8) { if ((s3->chip == S3_86C928) || (s3->chip == S3_86C928PCI)) s3_accel_out_pixtrans_l(s3, val); else { s3_accel_write_fifo(s3, addr, val); s3_accel_write_fifo(s3, addr + 1, val >> 8); s3_accel_write_fifo(s3, addr + 2, val >> 16); s3_accel_write_fifo(s3, addr + 3, val >> 24); } } else { s3_accel_write_fifo(s3, addr, val); s3_accel_write_fifo(s3, addr + 1, val >> 8); s3_accel_write_fifo(s3, addr + 2, val >> 16); s3_accel_write_fifo(s3, addr + 3, val >> 24); } } else { s3_accel_out_pixtrans_l(s3, val); } } } static void s3_vblank_start(svga_t *svga) { s3_t *s3 = (s3_t *) svga->priv; s3->subsys_stat |= INT_VSY; s3_update_irqs(s3); } static uint32_t s3_hwcursor_convert_addr(svga_t *svga) { if ((svga->bpp == 8) && (((svga->gdcreg[5] & 0x60) == 0x20) || (svga->crtc[0x3a] & 0x10)) && (svga->crtc[0x45] & 0x10)) { if (svga->crtc[0x3a] & 0x10) return ((svga->hwcursor_latch.addr & 0xfffff1ff) | ((svga->hwcursor_latch.addr & 0x200) << 2)) | 0x600; else if ((svga->gdcreg[5] & 0x60) == 0x20) return ((svga->hwcursor_latch.addr & 0xfffff0ff) | ((svga->hwcursor_latch.addr & 0x300) << 2)) | 0x300; else return svga->hwcursor_latch.addr; } else return svga->hwcursor_latch.addr; } static void s3_hwcursor_draw(svga_t *svga, int displine) { const s3_t *s3 = (s3_t *) svga->priv; int shift = 1; int width = 16; uint16_t dat[2]; int xx; int offset = svga->hwcursor_latch.x - svga->hwcursor_latch.xoff; uint32_t fg; uint32_t bg; uint32_t real_addr; uint32_t remapped_addr; switch (svga->bpp) { case 15: fg = video_15to32[s3->hwc_fg_col & 0xffff]; bg = video_15to32[s3->hwc_bg_col & 0xffff]; if (s3->chip >= S3_86C928 && s3->chip <= S3_86C805) { if (s3->card_type != S3_MIROCRYSTAL10SD_805 && s3->card_type != S3_MIROCRYSTAL8S_805) { if (!(svga->crtc[0x45] & 0x04)) { shift = 2; width = 8; } } } break; case 16: fg = video_16to32[s3->hwc_fg_col & 0xffff]; bg = video_16to32[s3->hwc_bg_col & 0xffff]; if (s3->chip >= S3_86C928 && s3->chip <= S3_86C805) { if ((s3->card_type != S3_MIROCRYSTAL10SD_805) && (s3->card_type != S3_MIROCRYSTAL8S_805)) { if (!(svga->crtc[0x45] & 0x04)) { shift = 2; width = 8; } } else if (s3->card_type == S3_MIROCRYSTAL10SD_805) { if (!(svga->crtc[0x45] & 0x04)) { offset <<= 1; } } } break; case 24: if (s3->chip <= S3_86C805) { fg = svga->pallook[svga->crtc[0xe]]; bg = svga->pallook[svga->crtc[0xf]]; } else { fg = s3->hwc_fg_col; bg = s3->hwc_bg_col; } break; case 32: fg = s3->hwc_fg_col; bg = s3->hwc_bg_col; break; default: if (s3->chip >= S3_TRIO32) { fg = svga->pallook[s3->hwc_fg_col & 0xff]; bg = svga->pallook[s3->hwc_bg_col & 0xff]; } else { fg = svga->pallook[svga->crtc[0xe]]; bg = svga->pallook[svga->crtc[0xf]]; } break; } if (svga->interlace && svga->hwcursor_oddeven) svga->hwcursor_latch.addr += 16; real_addr = s3_hwcursor_convert_addr(svga); for (uint8_t x = 0; x < 64; x += 16) { remapped_addr = dword_remap(svga, real_addr); dat[0] = (svga->vram[remapped_addr & s3->vram_mask] << 8) | svga->vram[(remapped_addr + 1) & s3->vram_mask]; dat[1] = (svga->vram[(remapped_addr + 2) & s3->vram_mask] << 8) | svga->vram[(remapped_addr + 3) & s3->vram_mask]; if (svga->crtc[0x55] & 0x10) { /*X11*/ for (xx = 0; xx < 16; xx++) { if (offset >= 0) { if (dat[0] & 0x8000) buffer32->line[displine][offset + svga->x_add] = (dat[1] & 0x8000) ? fg : bg; } offset++; dat[0] <<= shift; dat[1] <<= shift; } } else { /*Windows*/ for (xx = 0; xx < width; xx++) { if (offset >= 0) { 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] <<= shift; dat[1] <<= shift; } } svga->hwcursor_latch.addr += 4; real_addr = s3_hwcursor_convert_addr(svga); } if (svga->interlace && !svga->hwcursor_oddeven) svga->hwcursor_latch.addr += 16; } #define CLAMP(x) \ do { \ if ((x) & ~0xff) \ x = ((x) < 0) ? 0 : 0xff; \ } while (0) #define DECODE_YCbCr() \ do { \ int c; \ \ for (c = 0; c < 2; c++) { \ uint8_t y1, y2; \ int8_t Cr, Cb; \ int dR, dG, 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, dG, 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, y2; \ int8_t U, V; \ int dR, dG, 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 (s3->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_trio64v_overlay_draw(svga_t *svga, int displine) { const s3_t *s3 = (s3_t *) svga->priv; int offset = (s3->streams.sec_x - s3->streams.pri_x) + 1; int h_acc = s3->streams.dda_horiz_accumulator; int r[8]; int g[8]; int b[8]; int x_size; int x_read = 4; int x_write = 4; uint32_t *p; uint8_t *src = &svga->vram[svga->overlay_latch.addr]; p = &(buffer32->line[displine][offset + svga->x_add]); if ((offset + s3->streams.sec_w) > s3->streams.pri_w) x_size = (s3->streams.pri_w - s3->streams.sec_x) + 1; else x_size = s3->streams.sec_w + 1; OVERLAY_SAMPLE(); for (int x = 0; x < x_size; x++) { *p++ = r[x_read] | (g[x_read] << 8) | (b[x_read] << 16); h_acc += s3->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 += (s3->streams.k2_horiz_scale - s3->streams.k1_horiz_scale); } } svga->overlay_latch.v_acc += s3->streams.k1_vert_scale; if (svga->overlay_latch.v_acc >= 0) { svga->overlay_latch.v_acc += (s3->streams.k2_vert_scale - s3->streams.k1_vert_scale); svga->overlay_latch.addr += s3->streams.sec_stride; } } static void s3_io_remove_alt(s3_t *s3) { if (!s3->translate) return; io_removehandler(0x4148, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x4548, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x4948, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x8148, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x8548, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x8948, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x8d48, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x9148, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x9548, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x9948, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x9d48, 0x0002, s3_accel_in, s3_accel_in_w, NULL, s3_accel_out, s3_accel_out_w, NULL, s3); io_removehandler(0xa148, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xa548, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xa948, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xad48, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); if (s3->chip >= S3_86C928) io_removehandler(0xb148, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); else io_removehandler(0xb148, 0x0002, s3_accel_in, s3_accel_in_w, NULL, s3_accel_out, s3_accel_out_w, s3_accel_out_l, s3); io_removehandler(0xb548, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xb948, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xbd48, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xd148, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xe148, 0x0004, s3_accel_in, s3_accel_in_w, s3_accel_in_l, s3_accel_out, s3_accel_out_w, s3_accel_out_l, s3); io_removehandler(0xe548, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xe948, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xed48, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); } static void s3_io_remove(s3_t *s3) { io_removehandler(0x03c0, 0x0020, s3_in, NULL, NULL, s3_out, NULL, NULL, s3); io_removehandler(0x42e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x46e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x4ae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x82e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x86e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x8ae8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x8ee8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x92e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x96e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x9ae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0x9ee8, 0x0002, s3_accel_in, s3_accel_in_w, NULL, s3_accel_out, s3_accel_out_w, NULL, s3); io_removehandler(0xa2e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xa6e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xaae8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xaee8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); if (s3->chip >= S3_86C928) io_removehandler(0xb2e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); else io_removehandler(0xb2e8, 0x0002, s3_accel_in, s3_accel_in_w, NULL, s3_accel_out, s3_accel_out_w, s3_accel_out_l, s3); io_removehandler(0xb6e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xbae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xbee8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xcae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xd2e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xe2e8, 0x0004, s3_accel_in, s3_accel_in_w, s3_accel_in_l, s3_accel_out, s3_accel_out_w, s3_accel_out_l, s3); io_removehandler(0xe6e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xeae8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xeee8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xfee8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); s3_io_remove_alt(s3); } static void s3_io_set_alt(s3_t *s3) { const svga_t *svga = &s3->svga; if (!s3->translate) return; if ((s3->chip == S3_VISION968 || s3->chip == S3_VISION868) && (svga->seqregs[9] & 0x80)) { return; } io_sethandler(0x4148, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x4548, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x4948, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); if (s3->chip == S3_TRIO64 || s3->chip >= S3_TRIO64V || s3->chip == S3_VISION968 || s3->chip == S3_VISION868) { io_sethandler(0x8148, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x8548, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x8948, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x8d48, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x9148, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x9548, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); } else { io_sethandler(0x8148, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x8548, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x8948, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x8d48, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x9148, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x9548, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); } if (s3->chip == S3_VISION968 || s3->chip == S3_VISION868) io_sethandler(0x9948, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); else io_sethandler(0x9948, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x9d48, 0x0002, s3_accel_in, s3_accel_in_w, NULL, s3_accel_out, s3_accel_out_w, NULL, s3); io_sethandler(0xa148, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xa548, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xa948, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xad48, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); if (s3->chip >= S3_86C928) io_sethandler(0xb148, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); else io_sethandler(0xb148, 0x0002, s3_accel_in, s3_accel_in_w, NULL, s3_accel_out, s3_accel_out_w, s3_accel_out_l, s3); io_sethandler(0xb548, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xb948, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xbd48, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xe148, 0x0004, s3_accel_in, s3_accel_in_w, s3_accel_in_l, s3_accel_out, s3_accel_out_w, s3_accel_out_l, s3); if (s3->chip == S3_VISION968 || s3->chip == S3_VISION868) { io_sethandler(0xd148, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xe548, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xe948, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xed48, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); } } static void s3_io_set(s3_t *s3) { const svga_t *svga = &s3->svga; s3_io_remove(s3); io_sethandler(0x03c0, 0x0020, s3_in, NULL, NULL, s3_out, NULL, NULL, s3); if ((s3->chip == S3_VISION968 || s3->chip == S3_VISION868) && (svga->seqregs[9] & 0x80)) { return; } io_sethandler(0x42e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x46e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x4ae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); if (s3->chip == S3_TRIO64 || s3->chip >= S3_TRIO64V || s3->chip == S3_VISION968 || s3->chip == S3_VISION868) { io_sethandler(0x82e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x86e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x8ae8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x8ee8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x92e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x96e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); } else { io_sethandler(0x82e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x86e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x8ae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x8ee8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x92e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x96e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); } if (s3->chip == S3_VISION968 || s3->chip == S3_VISION868) io_sethandler(0x9ae8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); else io_sethandler(0x9ae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0x9ee8, 0x0002, s3_accel_in, s3_accel_in_w, NULL, s3_accel_out, s3_accel_out_w, NULL, s3); io_sethandler(0xa2e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xa6e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xaae8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xaee8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); if (s3->chip >= S3_86C928) io_sethandler(0xb2e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); else io_sethandler(0xb2e8, 0x0002, s3_accel_in, s3_accel_in_w, NULL, s3_accel_out, s3_accel_out_w, s3_accel_out_l, s3); io_sethandler(0xb6e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xbae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xbee8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xcae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xe2e8, 0x0004, s3_accel_in, s3_accel_in_w, s3_accel_in_l, s3_accel_out, s3_accel_out_w, s3_accel_out_l, s3); if (s3->chip == S3_VISION968 || s3->chip == S3_VISION868) { io_sethandler(0xd2e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xe6e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xeae8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xeee8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); } io_sethandler(0xfee8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); s3_io_set_alt(s3); } static void s3_out(uint16_t addr, uint8_t val, void *priv) { s3_t *s3 = (s3_t *) priv; svga_t *svga = &s3->svga; uint8_t old; int rs2; int rs3; if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1)) addr ^= 0x60; switch (addr) { case 0x3c2: if ((s3->chip == S3_VISION964) || (s3->chip == S3_VISION968) || (s3->chip == S3_86C928)) { if ((s3->card_type != S3_SPEA_MERCURY_P64V) && (s3->card_type != S3_MIROVIDEO40SV_ERGO_968)) { if (((val >> 2) & 3) != 3) icd2061_write(svga->clock_gen, (val >> 2) & 3); } } break; case 0x3c5: if (svga->seqaddr >= 0x10 && svga->seqaddr < 0x20) { svga->seqregs[svga->seqaddr] = val; switch (svga->seqaddr) { case 0x12: case 0x13: svga_recalctimings(svga); return; default: break; } } if (svga->seqaddr == 4) /*Chain-4 - update banking*/ { if (val & 0x08) svga->write_bank = svga->read_bank = s3->bank << 16; else svga->write_bank = svga->read_bank = s3->bank << 14; } else if (svga->seqaddr == 9) { svga->seqregs[svga->seqaddr] = val & 0x80; s3_io_set(s3); return; } else if (svga->seqaddr == 0xa) { svga->seqregs[svga->seqaddr] = val & 0x80; return; } else if (s3->chip >= S3_VISION964) { 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 = ((s3->chip >= S3_VISION964) && (svga->seqregs[0x0d] & 0x50)) || (svga->crtc[0x56] & ((s3->chip >= S3_TRIO32) ? 0x06 : 0x20)); svga_recalctimings(svga); return; } } break; case 0x3C6: case 0x3C7: case 0x3C8: case 0x3C9: rs2 = (svga->crtc[0x55] & 0x01) || !!(svga->crtc[0x43] & 2); if (s3->chip >= S3_TRIO32) svga_out(addr, val, svga); else if ((s3->chip == S3_VISION964 && s3->card_type != S3_ELSAWIN2KPROX_964) || (s3->chip == S3_86C928)) { rs3 = !!(svga->crtc[0x55] & 0x02); bt48x_ramdac_out(addr, rs2, rs3, val, svga->ramdac, svga); } else if ((s3->chip == S3_VISION964 && s3->card_type == S3_ELSAWIN2KPROX_964) || (s3->chip == S3_VISION968 && (s3->card_type == S3_ELSAWIN2KPROX || s3->card_type == S3_PHOENIX_VISION968 || s3->card_type == S3_NUMBER9_9FX_771))) ibm_rgb528_ramdac_out(addr, rs2, val, svga->ramdac, svga); else if (s3->chip == S3_VISION968 && (s3->card_type == S3_SPEA_MERCURY_P64V || s3->card_type == S3_MIROVIDEO40SV_ERGO_968)) { rs3 = !!(svga->crtc[0x55] & 0x02); tvp3026_ramdac_out(addr, rs2, rs3, val, svga->ramdac, svga); } else if (((s3->chip == S3_86C801) || (s3->chip == S3_86C805) || (s3->chip == S3_86C924)) && ((s3->card_type != S3_MIROCRYSTAL10SD_805) && (s3->card_type != S3_MIROCRYSTAL8S_805))) att49x_ramdac_out(addr, rs2, val, svga->ramdac, svga); else if (s3->chip == S3_86C911) { sc1148x_ramdac_out(addr, rs2, val, svga->ramdac, svga); } else if (s3->card_type == S3_NUMBER9_9FX_531) att498_ramdac_out(addr, rs2, val, svga->ramdac, svga); else if ((s3->chip == S3_86C928PCI) && (s3->card_type == S3_SPEA_MERCURY_LITE_PCI)) sc1502x_ramdac_out(addr, val, svga->ramdac, svga); else sdac_ramdac_out(addr, rs2, val, svga->ramdac, svga); return; case 0x3D4: svga->crtcreg = (s3->chip == S3_TRIO64V2) ? val : (val & 0x7f); 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 ((s3->chip == S3_TRIO64V2) && (svga->crtcreg >= 0x80)) return; if ((s3->chip <= S3_86C924) && (svga->crtcreg >= 0x50)) return; old = svga->crtc[svga->crtcreg]; svga->crtc[svga->crtcreg] = val; switch (svga->crtcreg) { case 0x31: s3->ma_ext = (s3->ma_ext & 0x1c) | ((val & 0x30) >> 4); svga->force_dword_mode = !!(val & 0x08); break; case 0x40: s3->enable_8514 = (val & 0x01); break; case 0x50: s3->bpp = (svga->crtc[0x50] >> 4) & 3; if (s3->bpp == 3) { if (!(s3->accel.multifunc[0xe] & 0x200)) /*On True Color mode change, reset bit 4 of Misc Index register*/ s3->accel.multifunc[0xe] &= ~0x10; } break; case 0x5c: if ((val & 0xa0) == 0x80) i2c_gpio_set(s3->i2c, !!(val & 0x40), !!(val & 0x10)); if (s3->card_type == S3_PHOENIX_VISION868 || s3->card_type == S3_PHOENIX_VISION968) { if ((val & 0x20) && (!(svga->crtc[0x55] & 0x01) && !(svga->crtc[0x43] & 2))) svga->dac_addr |= 0x20; } else if (s3->card_type == S3_MIROVIDEO40SV_ERGO_968) { if ((val & 0x80) && (!(svga->crtc[0x55] & 0x01) && !(svga->crtc[0x43] & 2))) svga->dac_addr |= 0x02; } break; case 0x69: if (s3->chip >= S3_VISION964) s3->ma_ext = val & 0x1f; break; case 0x35: s3->bank = (s3->bank & 0x70) | (val & 0xf); if (svga->chain4) svga->write_bank = svga->read_bank = s3->bank << 16; else svga->write_bank = svga->read_bank = s3->bank << 14; break; case 0x51: if (s3->chip == S3_86C801 || s3->chip == S3_86C805) { s3->bank = (s3->bank & 0x6f) | ((val & 0x4) << 2); s3->ma_ext = (s3->ma_ext & ~0x4) | ((val & 1) << 2); } else { s3->bank = (s3->bank & 0x4f) | ((val & 0xc) << 2); s3->ma_ext = (s3->ma_ext & ~0xc) | ((val & 3) << 2); } if (svga->chain4) svga->write_bank = svga->read_bank = s3->bank << 16; else svga->write_bank = svga->read_bank = s3->bank << 14; break; case 0x6a: if (s3->chip >= S3_VISION964) { s3->bank = val; if (svga->chain4) svga->write_bank = svga->read_bank = s3->bank << 16; else svga->write_bank = svga->read_bank = s3->bank << 14; } break; case 0x45: if (s3->chip == S3_VISION964 || s3->chip == S3_VISION968) break; svga->hwcursor.ena = val & 1; break; case 0x46: case 0x47: case 0x48: case 0x49: case 0x4c: case 0x4d: case 0x4e: case 0x4f: if (s3->chip == S3_VISION964 || s3->chip == S3_VISION968) break; svga->hwcursor.x = ((svga->crtc[0x46] << 8) | svga->crtc[0x47]) & 0x7ff; if (svga->bpp == 32) svga->hwcursor.x >>= 1; 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; svga->hwcursor.addr = ((((svga->crtc[0x4c] << 8) | svga->crtc[0x4d]) & 0xfff) * 1024) + (svga->hwcursor.yoff * 16); if ((s3->chip >= S3_TRIO32) && svga->bpp == 32) svga->hwcursor.x <<= 1; else if ((s3->chip >= S3_86C928 && s3->chip <= S3_86C805) && (svga->bpp == 15 || svga->bpp == 16)) { if ((s3->card_type == S3_MIROCRYSTAL10SD_805) && !(svga->crtc[0x45] & 0x04) && svga->bpp == 16) svga->hwcursor.x >>= 2; else svga->hwcursor.x >>= 1; } else if ((s3->chip >= S3_86C928 && s3->chip <= S3_86C805) && (svga->bpp == 24)) svga->hwcursor.x /= 3; else if ((s3->chip <= S3_86C805) && s3->color_16bit) svga->hwcursor.x >>= 1; break; case 0x4a: switch (s3->hwc_col_stack_pos) { case 0: s3->hwc_fg_col = (s3->hwc_fg_col & 0xffff00) | val; break; case 1: s3->hwc_fg_col = (s3->hwc_fg_col & 0xff00ff) | (val << 8); break; case 2: s3->hwc_fg_col = (s3->hwc_fg_col & 0x00ffff) | (val << 16); break; default: break; } s3->hwc_col_stack_pos = (s3->hwc_col_stack_pos + 1) & 3; break; case 0x4b: switch (s3->hwc_col_stack_pos) { case 0: s3->hwc_bg_col = (s3->hwc_bg_col & 0xffff00) | val; break; case 1: s3->hwc_bg_col = (s3->hwc_bg_col & 0xff00ff) | (val << 8); break; case 2: s3->hwc_bg_col = (s3->hwc_bg_col & 0x00ffff) | (val << 16); break; default: break; } s3->hwc_col_stack_pos = (s3->hwc_col_stack_pos + 1) & 3; break; case 0x53: case 0x58: case 0x59: case 0x5a: s3_updatemapping(s3); break; case 0x55: if (s3->chip == S3_86C928) { if (val & 0x28) { svga->hwcursor_draw = NULL; svga->dac_hwcursor_draw = bt48x_hwcursor_draw; } else { svga->hwcursor_draw = s3_hwcursor_draw; svga->dac_hwcursor_draw = NULL; } } break; case 0x42: if ((s3->chip == S3_VISION964) || (s3->chip == S3_VISION968) || (s3->chip == S3_86C928)) { if (((svga->miscout >> 2) & 3) == 3) icd2061_write(svga->clock_gen, svga->crtc[0x42] & 0x0f); } break; case 0x43: if (s3->chip < S3_VISION964) { if (s3->chip <= S3_86C805) { s3->color_16bit = !!(val & 8); if (s3->color_16bit) { s3->width = 1024; } else { if (s3->chip <= S3_86C924) s3->width = 1024; else { if (s3->accel.advfunc_cntl & 4) s3->width = 1024; else s3->width = 640; } } } s3_io_remove_alt(s3); s3->translate = !!(val & 0x10); s3_io_set_alt(s3); } break; case 0x56: svga->dpms = ((s3->chip >= S3_VISION964) && (svga->seqregs[0x0d] & 0x50)) || (svga->crtc[0x56] & ((s3->chip >= S3_TRIO32) ? 0x06 : 0x20)); old = ~val; /* force recalc */ break; case 0x67: if (s3->chip >= S3_TRIO32) { switch (val >> 4) { case 3: svga->bpp = 15; break; case 5: svga->bpp = 16; break; case 7: svga->bpp = 24; break; case 13: svga->bpp = 32; break; default: svga->bpp = 8; break; } } break; 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) && (s3->chip >= S3_TRIO64V)) || (s3->chip < S3_TRIO64V)) svga->ma_latch |= (s3->ma_ext << 16); } else { svga->fullchange = svga->monitor->mon_changeframecount; svga_recalctimings(svga); } } } break; default: break; } svga_out(addr, val, svga); } static uint8_t s3_in(uint16_t addr, void *priv) { s3_t *s3 = (s3_t *) priv; svga_t *svga = &s3->svga; int rs2; int rs3; uint8_t temp; if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1)) addr ^= 0x60; switch (addr) { case 0x3c1: if (svga->attraddr > 0x14) return 0xff; break; case 0x3c2: if (s3->chip <= S3_86C924) return svga_in(addr, svga) | 0x10; break; case 0x3c5: if (svga->seqaddr >= 0x10 && svga->seqaddr < 0x20) { temp = svga->seqregs[svga->seqaddr]; /* This is needed for the Intel Advanced/ATX's built-in S3 Trio64V+ BIOS to not get stuck in an infinite loop. */ if (((s3->card_type == S3_STB_POWERGRAPH_64_VIDEO) || (s3->card_type == S3_PHOENIX_TRIO64VPLUS_ONBOARD) || (s3->card_type == S3_CARDEX_TRIO64VPLUS)) && (svga->seqaddr == 0x17)) svga->seqregs[svga->seqaddr] ^= 0x01; return temp; } break; case 0x3c6: case 0x3c7: case 0x3c8: case 0x3c9: rs2 = (svga->crtc[0x55] & 0x01) || !!(svga->crtc[0x43] & 2); if (s3->chip >= S3_TRIO32) return svga_in(addr, svga); else if ((s3->chip == S3_VISION964 && s3->card_type != S3_ELSAWIN2KPROX_964) || (s3->chip == S3_86C928)) { if (s3->chip == S3_86C928) rs3 = !!(svga->crtc[0x55] & 0x28) || !!(svga->crtc[0x45] & 0x20) || !!(svga->crtc[0x55] & 0x02); /*Quite insane but Win95's S3 driver wants it set at all costs for 8bpp+ mode*/ else rs3 = !!(svga->crtc[0x55] & 0x02); temp = bt48x_ramdac_in(addr, rs2, rs3, svga->ramdac, svga); return temp; } else if ((s3->chip == S3_VISION964 && s3->card_type == S3_ELSAWIN2KPROX_964) || (s3->chip == S3_VISION968 && (s3->card_type == S3_ELSAWIN2KPROX || s3->card_type == S3_PHOENIX_VISION968 || s3->card_type == S3_NUMBER9_9FX_771))) return ibm_rgb528_ramdac_in(addr, rs2, svga->ramdac, svga); else if (s3->chip == S3_VISION968 && (s3->card_type == S3_SPEA_MERCURY_P64V || s3->card_type == S3_MIROVIDEO40SV_ERGO_968)) { rs3 = !!(svga->crtc[0x55] & 0x02); return tvp3026_ramdac_in(addr, rs2, rs3, svga->ramdac, svga); } else if (((s3->chip == S3_86C801) || (s3->chip == S3_86C805) || (s3->chip == S3_86C924)) && ((s3->card_type != S3_MIROCRYSTAL10SD_805) && (s3->card_type != S3_MIROCRYSTAL8S_805))) return att49x_ramdac_in(addr, rs2, svga->ramdac, svga); else if (s3->chip == S3_86C911) return sc1148x_ramdac_in(addr, rs2, svga->ramdac, svga); else if (s3->card_type == S3_NUMBER9_9FX_531) return att498_ramdac_in(addr, rs2, svga->ramdac, svga); else if ((s3->chip == S3_86C928PCI) && (s3->card_type == S3_SPEA_MERCURY_LITE_PCI)) return sc1502x_ramdac_in(addr, svga->ramdac, svga); else return sdac_ramdac_in(addr, rs2, svga->ramdac, svga); case 0x3d4: return svga->crtcreg; case 0x3d5: switch (svga->crtcreg) { case 0x2d: return (s3->chip == S3_TRIO64V2) ? 0x89 : 0x88; /*Extended chip ID*/ case 0x2e: return s3->id_ext; /*New chip ID*/ case 0x2f: switch (s3->chip) { /*Revision level*/ case S3_TRIO64V: return 0x40; case S3_TRIO64V2: return 0x16; /*Confirmed on an onboard 64V2/DX*/ default: return 0x00; } break; case 0x30: return s3->id; /*Chip ID*/ case 0x31: return (svga->crtc[0x31] & 0xcf) | ((s3->ma_ext & 3) << 4); case 0x35: return (svga->crtc[0x35] & 0xf0) | (s3->bank & 0xf); case 0x45: s3->hwc_col_stack_pos = 0; return svga->crtc[0x45]; case 0x51: return (svga->crtc[0x51] & 0xf0) | ((s3->bank >> 2) & 0xc) | ((s3->ma_ext >> 2) & 3); case 0x5c: /* General Output Port Register */ temp = svga->crtc[svga->crtcreg] & 0xa0; if (((svga->miscout >> 2) & 3) == 3) temp |= svga->crtc[0x42] & 0x0f; else temp |= ((svga->miscout >> 2) & 3); if ((temp & 0xa0) == 0xa0) { if ((svga->crtc[0x5c] & 0x40) && i2c_gpio_get_scl(s3->i2c)) temp |= 0x40; if ((svga->crtc[0x5c] & 0x10) && i2c_gpio_get_sda(s3->i2c)) temp |= 0x10; } return temp; case 0x69: return s3->ma_ext; case 0x6a: return s3->bank; /* Phoenix S3 video BIOS'es seem to expect CRTC registers 6B and 6C to be mirrors of 59 and 5A. */ case 0x6b: if (s3->chip != S3_TRIO64V2) { if (svga->crtc[0x53] & 0x08) { return (s3->chip == S3_TRIO64V) ? (svga->crtc[0x59] & 0xfc) : (svga->crtc[0x59] & 0xfe); } else { return svga->crtc[0x59]; } } else return svga->crtc[0x6b]; break; case 0x6c: if (s3->chip != S3_TRIO64V2) { if (svga->crtc[0x53] & 0x08) { return 0x00; } else return (svga->crtc[0x5a] & 0x80); } else return svga->crtc[0x6c]; break; default: break; } return svga->crtc[svga->crtcreg]; default: break; } return svga_in(addr, svga); } static void s3_recalctimings(svga_t *svga) { s3_t *s3 = (s3_t *) svga->priv; int clk_sel = (svga->miscout >> 2) & 3; uint8_t mask = 0xc0; if (svga->crtc[0x33] & 0x20) { /* 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; } else { 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); } } svga->hdisp = svga->hdisp_old; svga->ma_latch |= (s3->ma_ext << 16); if (s3->chip >= S3_86C928) { 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; else svga->vblankstart = svga->dispend; if (svga->crtc[0x5e] & 0x10) svga->vsyncstart |= 0x400; if (svga->crtc[0x5e] & 0x40) svga->split |= 0x400; if (s3->accel.advfunc_cntl & 0x01) svga->split = 0x7fff; if (svga->crtc[0x51] & 0x30) svga->rowoffset |= (svga->crtc[0x51] & 0x30) << 4; else if (svga->crtc[0x43] & 0x04) svga->rowoffset |= 0x100; } else if (svga->crtc[0x43] & 0x04) svga->rowoffset |= 0x100; if (!svga->rowoffset) svga->rowoffset = 0x100; if ((s3->chip == S3_VISION964) || (s3->chip == S3_86C928)) { if (s3->card_type == S3_ELSAWIN2KPROX_964) ibm_rgb528_recalctimings(svga->ramdac, svga); else { bt48x_recalctimings(svga->ramdac, svga); svga->interlace |= (!!(svga->crtc[0x42] & 0x20)); } } else if (s3->chip == S3_VISION968) { if ((s3->card_type == S3_SPEA_MERCURY_P64V) || (s3->card_type == S3_MIROVIDEO40SV_ERGO_968)) tvp3026_recalctimings(svga->ramdac, svga); else ibm_rgb528_recalctimings(svga->ramdac, svga); } else svga->interlace = !!(svga->crtc[0x42] & 0x20); if ((((svga->miscout >> 2) & 3) == 3) && (s3->chip < S3_TRIO32)) clk_sel = svga->crtc[0x42] & 0x0f; svga->clock = (cpuclock * (double) (1ULL << 32)) / svga->getclock(clk_sel, svga->clock_gen); switch (svga->crtc[0x67] >> 4) { case 3: case 5: case 7: svga->clock /= 2; break; default: break; } svga->lowres = (!!(svga->attrregs[0x10] & 0x40) && !(svga->crtc[0x3a] & 0x10)); if (s3->chip != S3_86C801) mask |= 0x01; switch (svga->crtc[0x50] & mask) { case 0x00: if (s3->color_16bit) s3->width = 1024; else s3->width = (svga->crtc[0x31] & 2) ? 2048 : 1024; break; case 0x01: s3->width = 1152; break; case 0x40: s3->width = 640; break; case 0x80: s3->width = ((s3->chip > S3_86C805) && (s3->accel.advfunc_cntl & 4)) ? 1600 : 800; break; case 0x81: s3->width = 1600; break; case 0xc0: s3->width = 1280; break; default: break; } if (svga->crtc[0x33] & 0x20) { /* 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; if (s3->chip >= S3_VISION964) svga->hblank_end_mask = 0x7f; } else if (s3->chip >= S3_86C801) { svga->hblankstart = (((svga->crtc[0x5d] & 0x04) >> 2) << 8) + svga->crtc[2]; if (s3->chip >= S3_VISION964) { /* NOTE: The S3 Trio64V+ datasheet says this is bit 7, but then where is bit 6? The datasheets for the pre-Trio64V+ cards say +64, which implies bit 6, and, contrary to VGADOC, it also exists on Trio32, Trio64, Vision868, and Vision968. */ 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; } } #ifdef OLD_CODE_REFERENCE if (s3->card_type == S3_MIROCRYSTAL10SD_805 || s3->card_type == S3_MIROCRYSTAL20SD_864 || s3->card_type == S3_MIROCRYSTAL20SV_964 || s3->card_type == S3_SPEA_MIRAGE_86C801 || s3->card_type == S3_SPEA_MIRAGE_86C805 || s3->card_type == S3_MIROCRYSTAL8S_805 || s3->card_type == S3_NUMBER9_9FX_531 || s3->card_type == S3_SPEA_MERCURY_LITE_PCI) { if (!(svga->crtc[0x5e] & 0x04)) svga->vblankstart = svga->dispend; if (svga->bpp != 32) { if (svga->crtc[0x31] & 2) s3->width = 2048; else { if (s3->card_type == S3_MIROCRYSTAL10SD_805) { if (svga->hdisp == 1280 && s3->width == 1024) { s3->width = 1280; } } } } else { if (s3->card_type == S3_NUMBER9_9FX_531) { if ((svga->hdisp == 1600) && (s3->width == 1600)) s3->width = 800; } } } else if (s3->chip == S3_86C928) { if (svga->bpp == 15) { if (s3->width == 800) s3->width = 1024; } } #endif if ((svga->crtc[0x3a] & 0x10) && !svga->lowres) { svga->vram_display_mask = s3->vram_mask; s3_log("BPP=%d, pitch=%d, width=%02x, double?=%x, 16bit?=%d, highres?=%d, " "attr=%02x.\n", svga->bpp, s3->width, svga->crtc[0x50], svga->crtc[0x31] & 0x02, s3->color_16bit, s3->accel.advfunc_cntl & 4, svga->attrregs[0x10] & 0x40); switch (svga->bpp) { case 8: svga->render = svga_render_8bpp_highres; switch (s3->chip) { case S3_86C928: switch (s3->card_type) { case S3_METHEUS_86C928: switch (s3->width) { case 1280: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; break; case 2048: /*Account for the 1280x1024 resolution*/ switch (svga->hdisp) { case 320: svga->hdisp <<= 2; svga->dots_per_clock <<= 2; break; case 640: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; break; default: break; } break; default: break; } break; default: break; } break; case S3_86C928PCI: switch (s3->card_type) { case S3_SPEA_MERCURY_LITE_PCI: switch (s3->width) { case 640: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; default: break; } break; default: break; } break; case S3_VISION964: switch (s3->card_type) { case S3_ELSAWIN2KPROX_964: switch (s3->width) { case 1280: case 1600: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; default: break; } break; default: break; } break; case S3_VISION968: switch (s3->card_type) { case S3_MIROVIDEO40SV_ERGO_968: if (svga->hdisp == 832) svga->hdisp -= 32; break; case S3_NUMBER9_9FX_771: case S3_PHOENIX_VISION968: case S3_SPEA_MERCURY_P64V: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; if (svga->hdisp == 832) svga->hdisp -= 32; break; case S3_ELSAWIN2KPROX: switch (s3->width) { case 1280: case 1600: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; default: break; } break; default: break; } break; default: break; } #ifdef OLD_CODE_REFERENCE if (s3->chip != S3_VISION868) { if (s3->chip == S3_86C928) { if (s3->width == 2048 || s3->width == 1280 || s3->width == 1600) { if ((s3->width != 1600) && (svga->dispend == 1024) && (svga->hdisp != 1280)) svga->hdisp <<= 2; else svga->hdisp <<= 1; } } else if ((s3->chip != S3_86C801) && (s3->chip != S3_86C805) && (s3->chip != S3_TRIO32) && (s3->chip != S3_TRIO64) && (s3->chip != S3_VISION964) && (s3->chip != S3_VISION968)) { if (s3->width == 1280 || s3->width == 1600) svga->hdisp <<= 1; } else if ((s3->card_type == S3_ELSAWIN2KPROX_964) || (s3->card_type == S3_ELSAWIN2KPROX)) { if (s3->width == 1280 || s3->width == 1600) svga->hdisp <<= 1; } else if (s3->card_type == S3_SPEA_MERCURY_P64V) { if (s3->width == 1280 || s3->width == 1600) svga->hdisp <<= 1; } } else if (s3->card_type == S3_NUMBER9_9FX_771) svga->hdisp <<= 1; } #endif break; case 15: svga->render = svga_render_15bpp_highres; switch (s3->chip) { case S3_86C911: case S3_86C924: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; case S3_86C801: switch (s3->card_type) { case S3_PHOENIX_86C801: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; default: break; } break; case S3_86C805: switch (s3->card_type) { case S3_MIROCRYSTAL8S_805: case S3_MIROCRYSTAL10SD_805: case S3_PHOENIX_86C805: case S3_86C805_ONBOARD: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; case S3_SPEA_MIRAGE_86C805: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; switch (s3->width) { case 800: case 1024: if (svga->hdisp == 400) { /*SPEA specific drivers + its VBE RAM BIOS...*/ svga->hdisp <<= 1; svga->dots_per_clock <<= 1; } break; default: break; } break; default: break; } break; case S3_86C928: switch (s3->card_type) { case S3_METHEUS_86C928: if (!s3->color_16bit) { svga->hdisp <<= 1; svga->dots_per_clock <<= 1; } switch (svga->hdisp) { /*This might be a driver issue*/ case 800: s3->width = 1024; break; case 1280: s3->width = 2048; break; default: break; } break; default: break; } break; case S3_86C928PCI: switch (s3->card_type) { case S3_SPEA_MERCURY_LITE_PCI: switch (s3->width) { case 640: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; default: break; } break; default: break; } break; case S3_VISION864: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; case S3_VISION964: switch (s3->card_type) { case S3_ELSAWIN2KPROX_964: switch (s3->width) { case 1280: case 1600: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; break; default: break; } break; default: break; } break; case S3_VISION868: switch (s3->card_type) { case S3_PHOENIX_VISION868: case S3_NUMBER9_9FX_531: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; default: break; } break; case S3_VISION968: switch (s3->card_type) { case S3_MIROVIDEO40SV_ERGO_968: if (svga->hdisp == 832) svga->hdisp -= 32; break; case S3_NUMBER9_9FX_771: case S3_PHOENIX_VISION968: case S3_SPEA_MERCURY_P64V: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; /* TODO: Is this still needed? */ if (svga->hdisp == 832) svga->hdisp -= 32; break; case S3_ELSAWIN2KPROX: switch (s3->width) { case 1280: case 1600: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; break; default: break; } break; default: break; } break; case S3_TRIO64: case S3_TRIO32: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; default: break; } #ifdef OLD_CODE_REFERENCE if ((s3->chip != S3_VISION964) && (s3->card_type != S3_SPEA_MIRAGE_86C801) && (s3->card_type != S3_SPEA_MIRAGE_86C805)) { if (s3->chip == S3_86C928) svga->hdisp <<= 1; else if (s3->chip != S3_VISION968) svga->hdisp >>= 1; } if ((s3->chip != S3_VISION868) && (s3->chip != S3_TRIO32) && (s3->chip != S3_TRIO64) && (s3->chip != S3_VISION964)) { if (s3->width == 1280 || s3->width == 1600) svga->hdisp <<= 1; else if (s3->card_type == S3_NUMBER9_9FX_771) svga->hdisp <<= 1; } if (s3->card_type == S3_MIROVIDEO40SV_ERGO_968 || s3->card_type == S3_PHOENIX_VISION968 || s3->card_type == S3_SPEA_MERCURY_P64V) { if (svga->hdisp == (1408 * 2)) svga->hdisp >>= 1; else svga->hdisp = s3->width; } if (s3->card_type == S3_SPEA_MIRAGE_86C801 || s3->card_type == S3_SPEA_MIRAGE_86C805 || s3->card_type == S3_SPEA_MERCURY_LITE_PCI) svga->hdisp = s3->width; #endif break; case 16: svga->render = svga_render_16bpp_highres; switch (s3->chip) { case S3_86C911: case S3_86C924: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; case S3_86C801: switch (s3->card_type) { case S3_PHOENIX_86C801: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; default: break; } break; case S3_86C805: switch (s3->card_type) { case S3_MIROCRYSTAL8S_805: case S3_MIROCRYSTAL10SD_805: case S3_PHOENIX_86C805: case S3_86C805_ONBOARD: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; case S3_SPEA_MIRAGE_86C805: svga->hdisp >>= 1; switch (s3->width) { case 800: case 1024: if (svga->hdisp == 400) { /*SPEA specific drivers + its VBE RAM BIOS...*/ svga->hdisp <<= 1; svga->dots_per_clock <<= 1; } break; default: break; } break; default: break; } break; case S3_86C928: switch (s3->card_type) { case S3_METHEUS_86C928: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; switch (svga->hdisp) { /*This might be a driver issue*/ case 800: s3->width = 1024; break; case 1280: s3->width = 2048; break; default: break; } break; default: break; } break; case S3_86C928PCI: switch (s3->card_type) { case S3_SPEA_MERCURY_LITE_PCI: switch (s3->width) { case 640: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; default: break; } break; default: break; } break; case S3_VISION864: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; case S3_VISION868: switch (s3->card_type) { case S3_PHOENIX_VISION868: case S3_NUMBER9_9FX_531: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; default: break; } break; case S3_VISION964: switch (s3->card_type) { case S3_ELSAWIN2KPROX_964: switch (s3->width) { case 1280: case 1600: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; break; default: break; } break; default: break; } break; case S3_VISION968: switch (s3->card_type) { case S3_MIROVIDEO40SV_ERGO_968: if (svga->hdisp == 832) svga->hdisp -= 32; break; case S3_NUMBER9_9FX_771: case S3_PHOENIX_VISION968: case S3_SPEA_MERCURY_P64V: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; /* TODO: Is this still needed? */ if (svga->hdisp == 832) svga->hdisp -= 32; break; case S3_ELSAWIN2KPROX: switch (s3->width) { case 1280: case 1600: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; break; default: break; } break; default: break; } break; case S3_TRIO64: case S3_TRIO32: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; default: break; } #ifdef OLD_CODE_REFERENCE if ((s3->card_type == S3_ELSAWIN2KPROX_964) || (s3->card_type == S3_ELSAWIN2KPROX)) { if (s3->width == 1280 || s3->width == 1600) svga->hdisp <<= 1; } if ((s3->chip != S3_VISION964) && (s3->card_type != S3_SPEA_MIRAGE_86C801) && (s3->card_type != S3_SPEA_MIRAGE_86C805)) { if (s3->chip == S3_86C928) svga->hdisp <<= 1; else if (s3->chip != S3_VISION968) svga->hdisp >>= 1; } else if ((s3->card_type == S3_SPEA_MIRAGE_86C801) || (s3->card_type == S3_SPEA_MIRAGE_86C805)) svga->hdisp >>= 1; if ((s3->chip != S3_VISION868) && (s3->chip != S3_TRIO32) && (s3->chip != S3_TRIO64) && (s3->chip != S3_VISION964)) { if (s3->width == 1280 || s3->width == 1600) svga->hdisp <<= 1; else if (s3->card_type == S3_NUMBER9_9FX_771) svga->hdisp <<= 1; } if (s3->card_type == S3_MIROVIDEO40SV_ERGO_968 || s3->card_type == S3_PHOENIX_VISION968 || s3->card_type == S3_SPEA_MERCURY_P64V) { if (svga->hdisp == (1408 << 1)) svga->hdisp >>= 1; else svga->hdisp = s3->width; } if (s3->card_type == S3_SPEA_MIRAGE_86C801 || s3->card_type == S3_SPEA_MIRAGE_86C805 || s3->card_type == S3_SPEA_MERCURY_LITE_PCI) svga->hdisp = s3->width; #endif break; case 24: svga->render = svga_render_24bpp_highres; switch (s3->chip) { case S3_86C924: switch (s3->card_type) { case S3_AMI_86C924: svga->hdisp = (svga->hdisp << 1) / 3; svga->dots_per_clock = (svga->dots_per_clock << 1) / 3; /* TODO: Is this still needed? */ if (svga->hdisp == 645) svga->hdisp -= 5; break; default: break; } break; case S3_86C801: switch (s3->card_type) { case S3_PHOENIX_86C801: case S3_SPEA_MIRAGE_86C801: svga->hdisp = (svga->hdisp << 1) / 3; svga->dots_per_clock = (svga->dots_per_clock << 1) / 3; break; default: break; } break; case S3_86C805: switch (s3->card_type) { case S3_MIROCRYSTAL8S_805: case S3_MIROCRYSTAL10SD_805: case S3_PHOENIX_86C805: case S3_SPEA_MIRAGE_86C805: case S3_86C805_ONBOARD: svga->hdisp = (svga->hdisp << 1) / 3; svga->dots_per_clock = (svga->dots_per_clock << 1) / 3; break; default: break; } break; case S3_86C928PCI: switch (s3->card_type) { case S3_SPEA_MERCURY_LITE_PCI: svga->hdisp = (svga->hdisp << 1) / 3; svga->dots_per_clock = (svga->dots_per_clock << 1) / 3; break; default: break; } break; case S3_VISION864: svga->hdisp = (svga->hdisp << 1) / 3; svga->dots_per_clock = (svga->dots_per_clock << 1) / 3; break; case S3_TRIO64: case S3_TRIO32: svga->hdisp /= 3; svga->dots_per_clock /= 3; break; default: break; } #ifdef OLD_CODE_REFERENCE if (s3->chip != S3_VISION968) { if (s3->chip != S3_86C928 && s3->chip != S3_86C801 && s3->chip != S3_86C805) svga->hdisp /= 3; else svga->hdisp = (svga->hdisp * 2) / 3; if (s3->card_type == S3_SPEA_MERCURY_LITE_PCI) { if (s3->width == 2048) { switch (svga->dispend) { case 480: svga->hdisp = 640; break; default: break; } } } else if (s3->chip == S3_86C924) { if (svga->dispend == 480) svga->hdisp = 640; } } else { if ((s3->card_type == S3_MIROVIDEO40SV_ERGO_968) || (s3->card_type == S3_PHOENIX_VISION968) || (s3->card_type == S3_SPEA_MERCURY_P64V)) svga->hdisp = s3->width; } #endif break; case 32: svga->render = svga_render_32bpp_highres; switch (s3->chip) { case S3_VISION868: switch (s3->card_type) { case S3_PHOENIX_VISION868: case S3_NUMBER9_9FX_531: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; default: break; } break; case S3_VISION964: switch (s3->card_type) { case S3_MIROCRYSTAL20SV_964: switch (s3->width) { case 800: case 1024: svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; default: break; } break; case S3_ELSAWIN2KPROX_964: switch (s3->width) { case 1280: case 1600: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; break; default: break; } break; default: break; } break; case S3_VISION968: switch (s3->card_type) { case S3_MIROVIDEO40SV_ERGO_968: if (svga->hdisp == 832) svga->hdisp -= 32; break; case S3_NUMBER9_9FX_771: case S3_PHOENIX_VISION968: case S3_SPEA_MERCURY_P64V: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; /* TODO: Is this still needed? */ if (svga->hdisp == 832) svga->hdisp -= 32; break; case S3_ELSAWIN2KPROX: switch (s3->width) { case 1280: case 1600: svga->hdisp <<= 1; svga->dots_per_clock <<= 1; break; default: break; } break; default: break; } break; default: break; } #ifdef OLD_CODE_REFERENCE if ((s3->chip < S3_TRIO32) && (s3->chip != S3_VISION964) && (s3->chip != S3_VISION968) && (s3->chip != S3_86C928)) { if (s3->chip == S3_VISION868) svga->hdisp >>= 1; else svga->hdisp >>= 2; } if (s3->width == 1280 || s3->width == 1600 || (s3->card_type == S3_SPEA_MERCURY_P64V || s3->card_type == S3_NUMBER9_9FX_771)) svga->hdisp <<= 1; if (s3->card_type == S3_NUMBER9_9FX_771) { if (svga->hdisp == 832) svga->hdisp -= 32; } if (s3->card_type == S3_MIROVIDEO40SV_ERGO_968 || s3->card_type == S3_MIROCRYSTAL20SV_964 || s3->card_type == S3_MIROCRYSTAL20SD_864 || s3->card_type == S3_PHOENIX_VISION968 || s3->card_type == S3_SPEA_MERCURY_P64V) { svga->hdisp = s3->width; if (s3->card_type == S3_MIROCRYSTAL20SD_864 || s3->card_type == S3_MIROCRYSTAL20SV_964) { if (s3->width == 800 || s3->width == 1024 || s3->width == 1600) { switch (svga->dispend) { case 400: case 480: svga->hdisp = 640; break; case 576: if (s3->width == 1600) s3->width = 800; svga->hdisp = 768; break; case 600: if (s3->width == 1600) s3->width = 800; svga->hdisp = 800; break; default: break; } } } } #endif break; default: break; } } else { svga->vram_display_mask = (svga->crtc[0x32] & 0x40) ? 0x3ffff : s3->vram_mask; if (!svga->scrblank && (svga->crtc[0x17] & 0x80) && svga->attr_palette_enable) { if ((svga->gdcreg[6] & 1) || (svga->attrregs[0x10] & 1)) { if (svga->crtc[0x31] & 0x08) { svga->vram_display_mask = s3->vram_mask; if (svga->bpp == 8) { /*Enhanced 4bpp mode, just like the 8bpp mode per the spec. */ svga->render = svga_render_8bpp_highres; svga->rowoffset <<= 1; } } } } } if ((s3->chip == S3_TRIO32) || (s3->chip == S3_TRIO64)) svga->hoverride = 1; else svga->hoverride = 0; } static void s3_trio64v_recalctimings(svga_t *svga) { s3_t *s3 = (s3_t *) svga->priv; int clk_sel = (svga->miscout >> 2) & 3; 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; } svga->hdisp = svga->hdisp_old; 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; svga->clock = (cpuclock * (double) (1ULL << 32)) / svga->getclock(clk_sel, svga->clock_gen); switch (svga->crtc[0x50] & 0xc1) { case 0x00: s3->width = (svga->crtc[0x31] & 2) ? 2048 : 1024; break; case 0x01: s3->width = 1152; break; case 0x40: s3->width = 640; break; case 0x80: s3->width = (s3->accel.advfunc_cntl & 4) ? 1600 : 800; break; case 0x81: s3->width = 1600; break; case 0xc0: s3->width = 1280; break; default: break; } 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)*/; 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; } else { svga->hblankstart = (((svga->crtc[0x5d] & 0x04) >> 2) << 8) + svga->crtc[2]; /* NOTE: The S3 Trio64V+ datasheet says this is bit 7, but then where is bit 6? The datasheets for the pre-Trio64V+ cards say +64, which implies bit 6, and, contrary to VGADOC, it also exists on Trio32, Trio64, Vision868, and Vision968. */ 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; } if ((svga->crtc[0x67] & 0xc) != 0xc) /*VGA mode*/ { svga->ma_latch |= (s3->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->attrregs[0x10] & 0x40) && !(svga->crtc[0x3a] & 0x10)); if ((svga->crtc[0x3a] & 0x10) && !svga->lowres) { svga->vram_display_mask = s3->vram_mask; switch (svga->bpp) { case 8: svga->render = svga_render_8bpp_highres; break; case 15: svga->render = svga_render_15bpp_highres; svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; case 16: svga->render = svga_render_16bpp_highres; svga->hdisp >>= 1; svga->dots_per_clock >>= 1; break; case 24: svga->render = svga_render_24bpp_highres; svga->hdisp /= 3; svga->dots_per_clock /= 3; break; case 32: svga->render = svga_render_32bpp_highres; break; default: break; } } else svga->vram_display_mask = (svga->crtc[0x32] & 0x40) ? 0x3ffff : s3->vram_mask; } else /*Streams mode*/ { if (s3->streams.buffer_ctrl & 1) svga->ma_latch = s3->streams.pri_fb1 >> 2; else svga->ma_latch = s3->streams.pri_fb0 >> 2; svga->hdisp = s3->streams.pri_w + 1; if (s3->streams.pri_h < svga->dispend) svga->dispend = s3->streams.pri_h; svga->overlay.x = s3->streams.sec_x - s3->streams.pri_x; svga->overlay.y = s3->streams.sec_y - s3->streams.pri_y; svga->overlay.cur_ysize = s3->streams.sec_h; if (s3->streams.buffer_ctrl & 2) svga->overlay.addr = s3->streams.sec_fb1; else svga->overlay.addr = s3->streams.sec_fb0; svga->overlay.ena = (svga->overlay.x >= 0); svga->overlay.v_acc = s3->streams.dda_vert_accumulator; svga->rowoffset = s3->streams.pri_stride >> 3; svga->vram_display_mask = s3->vram_mask; switch ((s3->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->multiplier = 0.5; svga->render = svga_render_15bpp_highres; break; case 5: /*RGB-16 (5.6.5)*/ svga->multiplier = 0.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; default: break; } } svga->hoverride = 1; } static void s3_updatemapping(s3_t *s3) { svga_t *svga = &s3->svga; if (s3->pci && !(s3->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_MEM)) { mem_mapping_disable(&svga->mapping); mem_mapping_disable(&s3->linear_mapping); mem_mapping_disable(&s3->mmio_mapping); mem_mapping_disable(&s3->new_mmio_mapping); return; } /*Banked framebuffer*/ if (svga->crtc[0x31] & 0x08) /*Enhanced mode mappings*/ { /* Enhanced mode forces 64kb at 0xa0000*/ mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x10000); svga->banked_mask = 0xffff; } else switch (svga->gdcreg[6] & 0xc) { /*VGA mapping*/ 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; default: break; } if (s3->chip >= S3_86C928) { s3->linear_base = (svga->crtc[0x5a] << 16) | (svga->crtc[0x59] << 24); if (s3->chip >= S3_86C928 && s3->chip <= S3_86C805) { if (s3->vlb) s3->linear_base &= 0x03ffffff; else s3->linear_base &= 0x00ffffff; } if ((svga->crtc[0x58] & 0x10) || (s3->accel.advfunc_cntl & 0x10)) { /*Linear framebuffer*/ mem_mapping_disable(&svga->mapping); switch (svga->crtc[0x58] & 3) { case 0: /*64k*/ s3->linear_size = 0x10000; break; case 1: /*1mb*/ s3->linear_size = 0x100000; break; case 2: /*2mb*/ s3->linear_size = 0x200000; break; case 3: /*8mb*/ switch (s3->chip) { /* Not on video cards that don't support 4MB*/ case S3_TRIO64: case S3_TRIO64V: case S3_TRIO64V2: case S3_86C928: case S3_86C928PCI: s3->linear_size = 0x400000; break; default: s3->linear_size = 0x800000; break; } break; default: break; } s3->linear_base &= ~(s3->linear_size - 1); if (s3->linear_base == 0xa0000) { mem_mapping_disable(&s3->linear_mapping); if (!(svga->crtc[0x53] & 0x10)) { mem_mapping_set_addr(&svga->mapping, s3->linear_base, 0x10000); svga->banked_mask = 0xffff; } } else { if (s3->chip >= S3_TRIO64V) s3->linear_base &= 0xfc000000; else if ((s3->chip == S3_VISION968) || (s3->chip == S3_VISION868)) s3->linear_base &= 0xfe000000; mem_mapping_set_addr(&s3->linear_mapping, s3->linear_base, s3->linear_size); } svga->fb_only = 1; } else { svga->fb_only = 0; mem_mapping_disable(&s3->linear_mapping); } /* Memory mapped I/O. */ if ((svga->crtc[0x53] & 0x10) || (s3->accel.advfunc_cntl & 0x20)) { mem_mapping_disable(&svga->mapping); if (s3->chip >= S3_TRIO64V) { if (svga->crtc[0x53] & 0x20) mem_mapping_set_addr(&s3->mmio_mapping, 0xb8000, 0x8000); else mem_mapping_set_addr(&s3->mmio_mapping, 0xa0000, 0x10000); } mem_mapping_enable(&s3->mmio_mapping); } else { mem_mapping_disable(&s3->mmio_mapping); } /* New MMIO. */ if (svga->crtc[0x53] & 0x08) mem_mapping_set_addr(&s3->new_mmio_mapping, s3->linear_base + 0x1000000, 0x20000); else mem_mapping_disable(&s3->new_mmio_mapping); } } static float s3_trio64_getclock(int clock, void *priv) { const s3_t *s3 = (s3_t *) priv; const svga_t *svga = &s3->svga; float t; int m; int n1; int n2; if (clock == 0) return 25175000.0; if (clock == 1) return 28322000.0; m = svga->seqregs[0x13] + 2; n1 = (svga->seqregs[0x12] & 0x1f) + 2; n2 = ((svga->seqregs[0x12] >> 5) & 0x07); t = (14318184.0 * ((float) m / (float) n1)) / (float) (1 << n2); return t; } static void s3_accel_out(uint16_t port, uint8_t val, void *priv) { s3_t *s3 = (s3_t *) priv; svga_t *svga = &s3->svga; if (port >= 0x8000) { if (!s3->enable_8514) return; if (s3_enable_fifo(s3)) s3_queue(s3, port, val, FIFO_OUT_BYTE); else s3_accel_out_fifo(s3, port, val); } else { switch (port) { case 0x4148: case 0x42e8: s3->subsys_stat &= ~val; s3_update_irqs(s3); break; case 0x4149: case 0x42e9: s3->subsys_cntl = val; s3_update_irqs(s3); break; case 0x4548: case 0x46e8: s3->accel.setup_md = val; break; case 0x4948: case 0x4ae8: s3->accel.advfunc_cntl = val; if ((s3->chip > S3_86C805) && ((svga->crtc[0x50] & 0xc1) == 0x80)) { s3->width = (val & 4) ? 1600 : 800; svga->fullchange = svga->monitor->mon_changeframecount; svga_recalctimings(svga); } else if (s3->chip <= S3_86C805) { svga->fullchange = svga->monitor->mon_changeframecount; svga_recalctimings(svga); } s3_updatemapping(s3); break; default: break; } } } static void s3_accel_out_w(uint16_t port, uint16_t val, void *priv) { s3_t *s3 = (s3_t *) priv; if (!s3->enable_8514) return; if (s3_enable_fifo(s3)) s3_queue(s3, port, val, FIFO_OUT_WORD); else s3_accel_out_fifo_w(s3, port, val); } static void s3_accel_out_l(uint16_t port, uint32_t val, void *priv) { s3_t *s3 = (s3_t *) priv; if (!s3->enable_8514) return; if (s3_enable_fifo(s3)) s3_queue(s3, port, val, FIFO_OUT_DWORD); else s3_accel_out_fifo_l(s3, port, val); } static uint8_t s3_accel_in(uint16_t port, void *priv) { s3_t *s3 = (s3_t *) priv; svga_t *svga = &s3->svga; int temp; uint8_t temp2; if (!s3->enable_8514) return 0xff; switch (port) { case 0x4148: case 0x42e8: return s3->subsys_stat; case 0x4149: case 0x42e9: return s3->subsys_cntl; case 0x8148: case 0x82e8: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.cur_y & 0xff; case 0x8149: case 0x82e9: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.cur_y >> 8; case 0x8548: case 0x86e8: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.cur_x & 0xff; case 0x8549: case 0x86e9: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.cur_x >> 8; case 0x8948: case 0x8ae8: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.desty_axstp & 0xff; } break; case 0x8949: case 0x8ae9: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.desty_axstp >> 8; } break; case 0x8d48: case 0x8ee8: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.destx_distp & 0xff; } break; case 0x8d49: case 0x8ee9: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.destx_distp >> 8; } break; case 0x9148: case 0x92e8: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.err_term & 0xff; case 0x9149: case 0x92e9: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.err_term >> 8; case 0x9548: case 0x96e8: if (s3->chip >= S3_86C928) { return s3->accel.maj_axis_pcnt & 0xff; } break; case 0x9549: case 0x96e9: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.maj_axis_pcnt >> 8; } break; case 0x8118: case 0x9948: case 0x9ae8: temp = 0; /* FIFO empty */ if (s3_enable_fifo(s3)) { if (!s3->blitter_busy) wake_fifo_thread(s3); if (FIFO_FULL) temp = 0xff; } return temp; case 0x8119: case 0x9949: case 0x9ae9: temp = 0; if (s3_enable_fifo(s3)) { if (!s3->blitter_busy) wake_fifo_thread(s3); if (!FIFO_EMPTY || s3->force_busy) temp |= 0x02; /*Hardware busy*/ else temp |= 0x04; /*FIFO empty*/ s3->force_busy = 0; if (s3->chip >= S3_VISION964) { if (FIFO_FULL) temp |= 0xf8; /*FIFO full*/ } if (s3->data_available) { temp |= 0x01; /*Read Data available*/ s3->data_available = 0; } } else { if (s3->force_busy) temp |= 0x02; /*Hardware busy*/ else { switch (s3->accel.cmd >> 13) { /*Some drivers may not set FIFO on but may still turn on FIFO empty bits!*/ case 0: if (!s3->accel.ssv_len) temp |= 0x04; break; case 1: if (!s3->accel.sy) temp |= 0x04; break; case 2: case 6: case 7: if (s3->accel.sy < 0) temp |= 0x04; break; default: break; } } s3->force_busy = 0; if (s3->data_available) { temp |= 0x01; /*Read Data available*/ s3->data_available = 0; } } return temp; case 0x9d48: case 0x9ee8: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.short_stroke & 0xff; } break; case 0x9d49: case 0x9ee9: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.short_stroke >> 8; } break; case 0xa148: case 0xa2e8: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.bkgd_color >> 16; else temp2 = s3->accel.bkgd_color & 0xff; return temp2; } break; case 0xa149: case 0xa2e9: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.bkgd_color >> 24; else temp2 = s3->accel.bkgd_color >> 8; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; } break; case 0xa14a: case 0xa2ea: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.bkgd_color & 0xff; else temp2 = s3->accel.bkgd_color >> 16; return temp2; case 0xa14b: case 0xa2eb: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.bkgd_color >> 8; else temp2 = s3->accel.bkgd_color >> 24; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; case 0xa548: case 0xa6e8: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.frgd_color >> 16; else temp2 = s3->accel.frgd_color & 0xff; return temp2; } break; case 0xa549: case 0xa6e9: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.frgd_color >> 24; else temp2 = s3->accel.frgd_color >> 8; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; } break; case 0xa54a: case 0xa6ea: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.frgd_color & 0xff; else temp2 = s3->accel.frgd_color >> 16; return temp2; case 0xa54b: case 0xa6eb: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.frgd_color >> 8; else temp2 = s3->accel.frgd_color >> 24; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; case 0xa948: case 0xaae8: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.wrt_mask >> 16; else temp2 = s3->accel.wrt_mask & 0xff; return temp2; } break; case 0xa949: case 0xaae9: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.wrt_mask >> 24; else temp2 = s3->accel.wrt_mask >> 8; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; } break; case 0xa94a: case 0xaaea: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.wrt_mask & 0xff; else temp2 = s3->accel.wrt_mask >> 16; return temp2; case 0xa94b: case 0xaaeb: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.wrt_mask >> 8; else temp2 = s3->accel.wrt_mask >> 24; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; case 0xad48: case 0xaee8: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.rd_mask >> 16; else temp2 = s3->accel.rd_mask & 0xff; return temp2; } break; case 0xad49: case 0xaee9: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.rd_mask >> 24; else temp2 = s3->accel.rd_mask >> 8; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; } break; case 0xad4a: case 0xaeea: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.rd_mask & 0xff; else temp2 = s3->accel.rd_mask >> 16; return temp2; case 0xad4b: case 0xaeeb: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.rd_mask >> 8; else temp2 = s3->accel.rd_mask >> 24; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; case 0xb148: case 0xb2e8: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.color_cmp >> 16; else temp2 = s3->accel.color_cmp & 0xff; return temp2; } break; case 0xb149: case 0xb2e9: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.color_cmp >> 24; else temp2 = s3->accel.color_cmp >> 8; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; } break; case 0xb14a: case 0xb2ea: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.color_cmp & 0xff; else temp2 = s3->accel.color_cmp >> 16; return temp2; case 0xb14b: case 0xb2eb: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.color_cmp >> 8; else temp2 = s3->accel.color_cmp >> 24; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; case 0xb548: case 0xb6e8: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.bkgd_mix; } break; case 0xb948: case 0xbae8: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.frgd_mix; } break; case 0xbd48: case 0xbee8: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); temp = s3->accel.multifunc[0xf] & 0xf; switch (temp) { case 0x0: return s3->accel.multifunc[0x0] & 0xff; case 0x1: return s3->accel.multifunc[0x1] & 0xff; case 0x2: return s3->accel.multifunc[0x2] & 0xff; case 0x3: return s3->accel.multifunc[0x3] & 0xff; case 0x4: return s3->accel.multifunc[0x4] & 0xff; case 0x5: return s3->accel.multifunc[0xa] & 0xff; case 0x6: return s3->accel.multifunc[0xe] & 0xff; case 0x7: return s3->accel.cmd & 0xff; case 0x8: return s3->accel.subsys_cntl & 0xff; case 0x9: return s3->accel.setup_md & 0xff; case 0xa: return s3->accel.multifunc[0xd] & 0xff; default: break; } return 0xff; } break; case 0xbd49: case 0xbee9: if (s3->chip >= S3_86C928) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); temp = s3->accel.multifunc[0xf] & 0xf; s3->accel.multifunc[0xf]++; switch (temp) { case 0x0: return s3->accel.multifunc[0x0] >> 8; case 0x1: return s3->accel.multifunc[0x1] >> 8; case 0x2: return s3->accel.multifunc[0x2] >> 8; case 0x3: return s3->accel.multifunc[0x3] >> 8; case 0x4: return s3->accel.multifunc[0x4] >> 8; case 0x5: return s3->accel.multifunc[0xa] >> 8; case 0x6: return s3->accel.multifunc[0xe] >> 8; case 0x7: return s3->accel.cmd >> 8; case 0x8: return (s3->accel.subsys_cntl >> 8) & ~0xe000; case 0x9: return (s3->accel.setup_md >> 8) & ~0xf000; case 0xa: return s3->accel.multifunc[0xd] >> 8; default: break; } return 0xff; } break; case 0xd148: case 0xd2e8: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.ropmix & 0xff; case 0xd149: case 0xd2e9: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.ropmix >> 8; case 0xe548: case 0xe6e8: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.pat_bg_color >> 16; else temp2 = s3->accel.pat_bg_color & 0xff; return temp2; case 0xe549: case 0xe6e9: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.pat_bg_color >> 24; else temp2 = s3->accel.pat_bg_color >> 8; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; case 0xe54a: case 0xe6ea: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.pat_bg_color & 0xff; else temp2 = s3->accel.pat_bg_color >> 16; return temp2; case 0xe54b: case 0xe6eb: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.pat_bg_color >> 8; else temp2 = s3->accel.pat_bg_color >> 24; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; case 0xe948: case 0xeae8: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.pat_y & 0xff; case 0xe949: case 0xeae9: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.pat_y >> 8; case 0xe94a: case 0xeaea: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.pat_x & 0xff; case 0xe94b: case 0xeaeb: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.pat_x >> 8; case 0xed48: case 0xeee8: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.pat_fg_color >> 16; else temp2 = s3->accel.pat_fg_color & 0xff; return temp2; case 0xed49: case 0xeee9: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.pat_fg_color >> 24; else temp2 = s3->accel.pat_fg_color >> 8; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; case 0xed4a: case 0xeeea: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.pat_fg_color & 0xff; else temp2 = s3->accel.pat_fg_color >> 16; return temp2; case 0xed4b: case 0xeeeb: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); if ((s3->bpp == 3) && (s3->accel.multifunc[0xe] & 0x10) && !(s3->accel.multifunc[0xe] & 0x200)) temp2 = s3->accel.pat_fg_color >> 8; else temp2 = s3->accel.pat_fg_color >> 24; if (!(s3->accel.multifunc[0xe] & 0x200)) s3->accel.multifunc[0xe] ^= 0x10; return temp2; case 0xe148: case 0xe2e8: if (!s3_cpu_dest(s3)) break; READ_PIXTRANS_BYTE_IO(0) if (s3->accel.cmd & 0x100) { switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(8, 1, s3->accel.pix_trans[0], 0, s3); else s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0], s3); } else s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0], s3); break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(16, 1, s3->accel.pix_trans[0], 0, s3); else s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[0], s3); } else s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[0], s3); break; default: break; } } return s3->accel.pix_trans[0]; case 0xe149: case 0xe2e9: if (!s3_cpu_dest(s3)) break; READ_PIXTRANS_BYTE_IO(1); if (s3->accel.cmd & 0x100) { switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(8, 1, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), 0, s3); else s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); } else s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) s3->accel_start(16, 1, s3->accel.pix_trans[1] | (s3->accel.pix_trans[0] << 8), 0, s3); else s3->accel_start(16, 1, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), 0, s3); } else { if (s3->accel.cmd & 0x1000) s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[1] | (s3->accel.pix_trans[0] << 8), s3); else s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); } } else { if (s3->accel.cmd & 0x1000) s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[1] | (s3->accel.pix_trans[0] << 8), s3); else s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); } break; default: break; } } return s3->accel.pix_trans[1]; case 0xe14a: case 0xe2ea: if (!s3_cpu_dest(s3)) break; READ_PIXTRANS_BYTE_IO(2); return s3->accel.pix_trans[2]; case 0xe14b: case 0xe2eb: if (!s3_cpu_dest(s3)) break; READ_PIXTRANS_BYTE_IO(3) if (s3->accel.cmd & 0x100) { switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(8, 1, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), 0, s3); else s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), s3); } else s3->accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), s3); break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(16, 1, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), 0, s3); else s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), s3); } else s3->accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8) | (s3->accel.pix_trans[2] << 16) | (s3->accel.pix_trans[3] << 24), s3); break; default: break; } } return s3->accel.pix_trans[3]; case 0xff20: case 0xff21: temp = s3->serialport & ~(SERIAL_PORT_SCR | SERIAL_PORT_SDR); if ((s3->serialport & SERIAL_PORT_SCW) && i2c_gpio_get_scl(s3->i2c)) temp |= SERIAL_PORT_SCR; if ((s3->serialport & SERIAL_PORT_SDW) && i2c_gpio_get_sda(s3->i2c)) temp |= SERIAL_PORT_SDR; return temp; default: break; } return 0xff; } static uint16_t s3_accel_in_w(uint16_t port, void *priv) { s3_t *s3 = (s3_t *) priv; svga_t *svga = &s3->svga; uint16_t temp = 0x0000; const uint16_t *vram_w = (uint16_t *) svga->vram; if (!s3->enable_8514) return 0xffff; if (port != 0x9ee8 && port != 0x9d48) { if (s3_cpu_dest(s3)) { READ_PIXTRANS_WORD switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) temp = (temp >> 8) | (temp << 8); s3->accel_start(8, 1, temp | (temp << 16), 0, s3); } else s3->accel_start(1, 1, 0xffffffff, temp | (temp << 16), s3); } else s3->accel_start(1, 1, 0xffffffff, temp | (temp << 16), s3); break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) temp = (temp >> 8) | (temp << 8); s3->accel_start(16, 1, temp | (temp << 16), 0, s3); } else s3->accel_start(2, 1, 0xffffffff, temp | (temp << 16), s3); } else { s3->accel_start(2, 1, 0xffffffff, temp | (temp << 16), s3); } break; default: break; } } } else { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); temp = s3->accel.short_stroke; } return temp; } static uint32_t s3_accel_in_l(UNUSED(uint16_t port), void *priv) { s3_t *s3 = (s3_t *) priv; svga_t *svga = &s3->svga; uint32_t temp = 0x00000000; const uint16_t *vram_w = (uint16_t *) svga->vram; if (!s3->enable_8514) return 0xffffffff; if (s3_cpu_dest(s3)) { READ_PIXTRANS_LONG switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) temp = ((temp & 0xff00ff00) >> 8) | ((temp & 0x00ff00ff) << 8); s3->accel_start(8, 1, temp, 0, s3); s3->accel_start(8, 1, temp >> 16, 0, s3); } else { s3->accel_start(1, 1, 0xffffffff, temp, s3); s3->accel_start(1, 1, 0xffffffff, temp >> 16, s3); } } else { s3->accel_start(1, 1, 0xffffffff, temp, s3); s3->accel_start(1, 1, 0xffffffff, temp >> 16, s3); } break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) temp = ((temp & 0xff00ff00) >> 8) | ((temp & 0x00ff00ff) << 8); s3->accel_start(16, 1, temp, 0, s3); s3->accel_start(16, 1, temp >> 16, 0, s3); } else { s3->accel_start(2, 1, 0xffffffff, temp, s3); s3->accel_start(2, 1, 0xffffffff, temp >> 16, s3); } } else { s3->accel_start(2, 1, 0xffffffff, temp, s3); s3->accel_start(2, 1, 0xffffffff, temp >> 16, s3); } break; default: break; } } return temp; } static void s3_accel_write(uint32_t addr, uint8_t val, void *priv) { s3_t *s3 = (s3_t *) priv; const svga_t *svga = &s3->svga; if (!s3->enable_8514) return; if (s3_enable_fifo(s3)) { if (svga->crtc[0x53] & 0x08) s3_queue(s3, addr & 0x1ffff, val, FIFO_WRITE_BYTE); else s3_queue(s3, addr & 0xffff, val, FIFO_WRITE_BYTE); } else s3_accel_write_fifo(s3, addr & 0xffff, val); } static void s3_accel_write_w(uint32_t addr, uint16_t val, void *priv) { s3_t *s3 = (s3_t *) priv; const svga_t *svga = &s3->svga; if (!s3->enable_8514) return; if (s3_enable_fifo(s3)) { if (svga->crtc[0x53] & 0x08) s3_queue(s3, addr & 0x1ffff, val, FIFO_WRITE_WORD); else s3_queue(s3, addr & 0xffff, val, FIFO_WRITE_WORD); } else s3_accel_write_fifo_w(s3, addr & 0xffff, val); } static void s3_accel_write_l(uint32_t addr, uint32_t val, void *priv) { s3_t *s3 = (s3_t *) priv; const svga_t *svga = &s3->svga; if (!s3->enable_8514) return; if (s3_enable_fifo(s3)) { if (svga->crtc[0x53] & 0x08) s3_queue(s3, addr & 0x1ffff, val, FIFO_WRITE_DWORD); else s3_queue(s3, addr & 0xffff, val, FIFO_WRITE_DWORD); } else s3_accel_write_fifo_l(s3, addr & 0xffff, val); } static uint8_t s3_accel_read(uint32_t addr, void *priv) { s3_t *s3 = (s3_t *) priv; svga_t *svga = &s3->svga; uint8_t temp = 0x00; if (!s3->enable_8514) return 0xff; if (svga->crtc[0x53] & 0x08) { if ((addr >= 0x08000) && (addr <= 0x0803f)) return s3_pci_read(0, addr & 0xff, s3); switch (addr & 0x1ffff) { case 0x83b0: case 0x83b1: case 0x83b2: case 0x83b3: case 0x83b4: case 0x83b5: case 0x83b6: case 0x83b7: case 0x83b8: case 0x83b9: case 0x83ba: case 0x83bb: case 0x83bc: case 0x83bd: case 0x83be: case 0x83bf: case 0x83c0: case 0x83c1: case 0x83c2: case 0x83c3: case 0x83c4: case 0x83c5: case 0x83c6: case 0x83c7: case 0x83c8: case 0x83c9: case 0x83ca: case 0x83cb: case 0x83cc: case 0x83cd: case 0x83ce: case 0x83cf: case 0x83d0: case 0x83d1: case 0x83d2: case 0x83d3: case 0x83d4: case 0x83d5: case 0x83d6: case 0x83d7: case 0x83d8: case 0x83d9: case 0x83da: case 0x83db: case 0x83dc: case 0x83dd: case 0x83de: case 0x83df: return s3_in(addr & 0x3ff, s3); case 0x8504: return s3->subsys_stat; case 0x8505: return s3->subsys_cntl; default: return s3_accel_in(addr & 0xffff, priv); } return 0xff; } else { if (addr & 0x8000) { temp = s3_accel_in(addr & 0xffff, priv); } else if (s3_cpu_dest(s3)) { READ_PIXTRANS_BYTE_MM switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(8, 1, temp | (temp << 8) | (temp << 16) | (temp << 24), 0, s3); else s3->accel_start(1, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); } else s3->accel_start(1, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(16, 1, temp | (temp << 8) | (temp << 16) | (temp << 24), 0, s3); else s3->accel_start(2, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); } else s3->accel_start(2, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); break; default: break; } } } return temp; } static uint16_t s3_accel_read_w(uint32_t addr, void *priv) { s3_t *s3 = (s3_t *) priv; svga_t *svga = &s3->svga; uint16_t temp = 0x0000; const uint16_t *vram_w = (uint16_t *) svga->vram; if (!s3->enable_8514) return 0xffff; if (svga->crtc[0x53] & 0x08) { switch (addr & 0x1fffe) { case 0x811c: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); return s3->accel.short_stroke; default: return s3_accel_read(addr, priv) | s3_accel_read(addr + 1, priv) << 8; } return 0xffff; } else { if (addr & 0x8000) { if (addr == 0x811c) { if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); temp = s3->accel.short_stroke; } else { temp = s3_accel_read((addr & 0xfffe), priv); temp |= s3_accel_read((addr & 0xfffe) + 1, priv) << 8; } } else if (s3_cpu_dest(s3)) { READ_PIXTRANS_WORD switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(8, 1, temp | (temp << 16), 0, s3); else s3->accel_start(1, 1, 0xffffffff, temp | (temp << 16), s3); } else s3->accel_start(1, 1, 0xffffffff, temp | (temp << 16), s3); break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) s3->accel_start(16, 1, temp | (temp << 16), 0, s3); else s3->accel_start(2, 1, 0xffffffff, temp | (temp << 16), s3); } else s3->accel_start(2, 1, 0xffffffff, temp | (temp << 16), s3); break; default: break; } } } return temp; } static uint32_t s3_accel_read_l(uint32_t addr, void *priv) { s3_t *s3 = (s3_t *) priv; svga_t *svga = &s3->svga; uint32_t temp = 0x00000000; const uint16_t *vram_w = (uint16_t *) svga->vram; if (!s3->enable_8514) return 0xffffffff; if (svga->crtc[0x53] & 0x08) { switch (addr & 0x1fffc) { case 0x8180: temp = s3->streams.pri_ctrl; break; case 0x8184: temp = s3->streams.chroma_ctrl; break; case 0x8190: temp = s3->streams.sec_ctrl; break; case 0x8194: temp = s3->streams.chroma_upper_bound; break; case 0x8198: temp = s3->streams.sec_filter; break; case 0x81a0: temp = s3->streams.blend_ctrl; break; case 0x81c0: temp = s3->streams.pri_fb0; break; case 0x81c4: temp = s3->streams.pri_fb1; break; case 0x81c8: temp = s3->streams.pri_stride; break; case 0x81cc: temp = s3->streams.buffer_ctrl; break; case 0x81d0: temp = s3->streams.sec_fb0; break; case 0x81d4: temp = s3->streams.sec_fb1; break; case 0x81d8: temp = s3->streams.sec_stride; break; case 0x81dc: temp = s3->streams.overlay_ctrl; break; case 0x81e0: temp = s3->streams.k1_vert_scale; break; case 0x81e4: temp = s3->streams.k2_vert_scale; break; case 0x81e8: temp = s3->streams.dda_vert_accumulator; break; case 0x81ec: temp = s3->streams.fifo_ctrl; break; case 0x81f0: temp = s3->streams.pri_start; break; case 0x81f4: temp = s3->streams.pri_size; break; case 0x81f8: temp = s3->streams.sec_start; break; case 0x81fc: temp = s3->streams.sec_size; break; case 0x18080: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); temp = 0; break; case 0x18088: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); temp = s3->videoengine.cntl; if (s3->bpp == 1) { /*The actual bpp is decided by the guest when idf is the same as odf*/ if (s3->videoengine.idf == 0 && s3->videoengine.odf == 0) { if (svga->bpp == 15) temp |= 0x600000; else temp |= 0x700000; } } else if (s3->bpp > 1) { if (s3->videoengine.idf == 0 && s3->videoengine.odf == 0) temp |= 0x300000; } break; case 0x1808c: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); temp = s3->videoengine.stretch_filt_const; break; case 0x18090: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); temp = s3->videoengine.src_dst_step; break; case 0x18094: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); temp = s3->videoengine.crop; break; case 0x18098: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); temp = s3->videoengine.src_base; break; case 0x1809c: if (s3_enable_fifo(s3)) s3_wait_fifo_idle(s3); temp = s3->videoengine.dest_base; if (s3->videoengine.busy) { temp |= (1 << 31); } else { temp &= ~(1 << 31); } break; default: temp = s3_accel_read_w(addr, priv) | (s3_accel_read_w(addr + 2, priv) << 16); break; } } else { if (addr & 0x8000) { temp = s3_accel_read((addr & 0xfffc), priv); temp |= s3_accel_read((addr & 0xfffc) + 1, priv) << 8; temp |= s3_accel_read((addr & 0xfffc) + 2, priv) << 16; temp |= s3_accel_read((addr & 0xfffc) + 3, priv) << 24; } else if (s3_cpu_dest(s3)) { READ_PIXTRANS_LONG switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { s3->accel_start(8, 1, temp, 0, s3); s3->accel_start(8, 1, temp >> 16, 0, s3); } else { s3->accel_start(1, 1, 0xffffffff, temp, s3); s3->accel_start(1, 1, 0xffffffff, temp >> 16, s3); } } else { s3->accel_start(1, 1, 0xffffffff, temp, s3); s3->accel_start(1, 1, 0xffffffff, temp >> 16, s3); } break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { s3->accel_start(16, 1, temp, 0, s3); s3->accel_start(16, 1, temp >> 16, 0, s3); } else { s3->accel_start(2, 1, 0xffffffff, temp, s3); s3->accel_start(2, 1, 0xffffffff, temp >> 16, s3); } } else { s3->accel_start(2, 1, 0xffffffff, temp, s3); s3->accel_start(2, 1, 0xffffffff, temp >> 16, s3); } break; default: break; } } } return temp; } static void polygon_setup(s3_t *s3) { if (s3->accel.point_1_updated) { int start_x = s3->accel.poly_cx; int start_y = s3->accel.poly_cy; int end_x = s3->accel.destx_distp << 20; int end_y = s3->accel.desty_axstp; if (end_y - start_y) s3->accel.poly_dx1 = (end_x - start_x) / (end_y - start_y); else s3->accel.poly_dx1 = 0; s3->accel.point_1_updated = 0; if (end_y == s3->accel.poly_cy) { s3->accel.poly_cx = end_x; s3->accel.poly_x = end_x >> 20; } } if (s3->accel.point_2_updated) { int start_x = s3->accel.poly_cx2; int start_y = s3->accel.poly_cy2; int end_x = s3->accel.x2 << 20; int end_y = s3->accel.desty_axstp2; if (end_y - start_y) s3->accel.poly_dx2 = (end_x - start_x) / (end_y - start_y); else s3->accel.poly_dx2 = 0; s3->accel.point_2_updated = 0; if (end_y == s3->accel.poly_cy) s3->accel.poly_cx2 = end_x; } } #define READ(addr, dat) \ if ((s3->bpp == 0) && !s3->color_16bit) \ dat = svga->vram[dword_remap(svga, addr) & s3->vram_mask]; \ else if ((s3->bpp == 1) || (s3->color_16bit && (svga->bpp < 24))) \ dat = vram_w[dword_remap_w(svga, addr) & (s3->vram_mask >> 1)]; \ else if (s3->bpp == 2) \ dat = svga->vram[dword_remap(svga, addr) & s3->vram_mask]; \ else if (s3->color_16bit && (svga->bpp == 24)) { \ dat = vram_w[dword_remap_w(svga, addr) & (s3->vram_mask >> 1)]; \ } else \ dat = vram_l[dword_remap_l(svga, addr) & (s3->vram_mask >> 2)]; #define MIX_READ \ { \ switch ((mix_dat & mix_mask) ? (s3->accel.frgd_mix & 0xf) : (s3->accel.bkgd_mix & 0xf)) { \ case 0x0: \ dest_dat = ~dest_dat; \ break; \ case 0x1: \ dest_dat = 0; \ break; \ case 0x2: \ dest_dat = ~0; \ break; \ case 0x3: \ dest_dat = dest_dat; \ break; \ case 0x4: \ dest_dat = ~src_dat; \ break; \ case 0x5: \ dest_dat = src_dat ^ dest_dat; \ break; \ case 0x6: \ dest_dat = ~(src_dat ^ dest_dat); \ break; \ case 0x7: \ dest_dat = src_dat; \ break; \ case 0x8: \ dest_dat = ~(src_dat & dest_dat); \ break; \ case 0x9: \ dest_dat = ~src_dat | dest_dat; \ break; \ case 0xa: \ dest_dat = src_dat | ~dest_dat; \ break; \ case 0xb: \ dest_dat = src_dat | dest_dat; \ break; \ case 0xc: \ dest_dat = src_dat & dest_dat; \ break; \ case 0xd: \ dest_dat = src_dat & ~dest_dat; \ break; \ case 0xe: \ dest_dat = ~src_dat & dest_dat; \ break; \ case 0xf: \ dest_dat = ~(src_dat | dest_dat); \ break; \ } \ } #define MIX \ { \ old_dest_dat = dest_dat; \ MIX_READ \ dest_dat = (dest_dat & wrt_mask) | (old_dest_dat & ~wrt_mask); \ } #define ROPMIX_READ(D, P, S) \ { \ switch (rop) { \ case 0x00: \ out = 0; \ break; \ case 0x01: \ out = ~(D | (P | S)); \ break; \ case 0x02: \ out = D & ~(P | S); \ break; \ case 0x03: \ out = ~(P | S); \ break; \ case 0x04: \ out = S & ~(D | P); \ break; \ case 0x05: \ out = ~(D | P); \ break; \ case 0x06: \ out = ~(P | ~(D ^ S)); \ break; \ case 0x07: \ out = ~(P | (D & S)); \ break; \ case 0x08: \ out = S & (D & ~P); \ break; \ case 0x09: \ out = ~(P | (D ^ S)); \ break; \ case 0x0a: \ out = D & ~P; \ break; \ case 0x0b: \ out = ~(P | (S & ~D)); \ break; \ case 0x0c: \ out = S & ~P; \ break; \ case 0x0d: \ out = ~(P | (D & ~S)); \ break; \ case 0x0e: \ out = ~(P | ~(D | S)); \ break; \ case 0x0f: \ out = ~P; \ break; \ case 0x10: \ out = P & ~(D | S); \ break; \ case 0x11: \ out = ~(D | S); \ break; \ case 0x12: \ out = ~(S | ~(D ^ P)); \ break; \ case 0x13: \ out = ~(S | (D & P)); \ break; \ case 0x14: \ out = ~(D | ~(P ^ S)); \ break; \ case 0x15: \ out = ~(D | (P & S)); \ break; \ case 0x16: \ out = P ^ (S ^ (D & ~(P & S))); \ break; \ case 0x17: \ out = ~(S ^ ((S ^ P) & (D ^ S))); \ break; \ case 0x18: \ out = (S ^ P) & (P ^ D); \ break; \ case 0x19: \ out = ~(S ^ (D & ~(P & S))); \ break; \ case 0x1a: \ out = P ^ (D | (S & P)); \ break; \ case 0x1b: \ out = ~(S ^ (D & (P ^ S))); \ break; \ case 0x1c: \ out = P ^ (S | (D & P)); \ break; \ case 0x1d: \ out = ~(D ^ (S & (P ^ D))); \ break; \ case 0x1e: \ out = P ^ (D | S); \ break; \ case 0x1f: \ out = ~(P & (D | S)); \ break; \ case 0x20: \ out = D & (P & ~S); \ break; \ case 0x21: \ out = ~(S | (D ^ P)); \ break; \ case 0x22: \ out = D & ~S; \ break; \ case 0x23: \ out = ~(S | (P & ~D)); \ break; \ case 0x24: \ out = (S ^ P) & (D ^ S); \ break; \ case 0x25: \ out = ~(P ^ (D & ~(S & P))); \ break; \ case 0x26: \ out = S ^ (D | (P & S)); \ break; \ case 0x27: \ out = S ^ (D | ~(P ^ S)); \ break; \ case 0x28: \ out = D & (P ^ S); \ break; \ case 0x29: \ out = ~(P ^ (S ^ (D | (P & S)))); \ break; \ case 0x2a: \ out = D & ~(P & S); \ break; \ case 0x2b: \ out = ~(S ^ ((S ^ P) & (P ^ D))); \ break; \ case 0x2c: \ out = S ^ (P & (D | S)); \ break; \ case 0x2d: \ out = P ^ (S | ~D); \ break; \ case 0x2e: \ out = P ^ (S | (D ^ P)); \ break; \ case 0x2f: \ out = ~(P & (S | ~D)); \ break; \ case 0x30: \ out = P & ~S; \ break; \ case 0x31: \ out = ~(S | (D & ~P)); \ break; \ case 0x32: \ out = S ^ (D | (P | S)); \ break; \ case 0x33: \ out = ~S; \ break; \ case 0x34: \ out = S ^ (P | (D & S)); \ break; \ case 0x35: \ out = S ^ (P | ~(D ^ S)); \ break; \ case 0x36: \ out = S ^ (D | P); \ break; \ case 0x37: \ out = ~(S & (D | P)); \ break; \ case 0x38: \ out = P ^ (S & (D | P)); \ break; \ case 0x39: \ out = S ^ (P | ~D); \ break; \ case 0x3a: \ out = S ^ (P | (D ^ S)); \ break; \ case 0x3b: \ out = ~(S & (P | ~D)); \ break; \ case 0x3c: \ out = P ^ S; \ break; \ case 0x3d: \ out = S ^ (P | ~(D | S)); \ break; \ case 0x3e: \ out = S ^ (P | (D & ~S)); \ break; \ case 0x3f: \ out = ~(P & S); \ break; \ case 0x40: \ out = P & (S & ~D); \ break; \ case 0x41: \ out = ~(D | (P ^ S)); \ break; \ case 0x42: \ out = (S ^ D) & (P ^ D); \ break; \ case 0x43: \ out = ~(S ^ (P & ~(D & S))); \ break; \ case 0x44: \ out = S & ~D; \ break; \ case 0x45: \ out = ~(D | (P & ~S)); \ break; \ case 0x46: \ out = D ^ (S | (P & D)); \ break; \ case 0x47: \ out = ~(P ^ (S & (D ^ P))); \ break; \ case 0x48: \ out = S & (D ^ P); \ break; \ case 0x49: \ out = ~(P ^ (D ^ (S | (P & D)))); \ break; \ case 0x4a: \ out = D ^ (P & (S | D)); \ break; \ case 0x4b: \ out = P ^ (D | ~S); \ break; \ case 0x4c: \ out = S & ~(D & P); \ break; \ case 0x4d: \ out = ~(S ^ ((S ^ P) | (D ^ S))); \ break; \ case 0x4e: \ out = P ^ (D | (S ^ P)); \ break; \ case 0x4f: \ out = ~(P & (D | ~S)); \ break; \ case 0x50: \ out = P & ~D; \ break; \ case 0x51: \ out = ~(D | (S & ~P)); \ break; \ case 0x52: \ out = D ^ (P | (S & D)); \ break; \ case 0x53: \ out = ~(S ^ (P & (D ^ S))); \ break; \ case 0x54: \ out = ~(D | ~(P | S)); \ break; \ case 0x55: \ out = ~D; \ break; \ case 0x56: \ out = D ^ (P | S); \ break; \ case 0x57: \ out = ~(D & (P | S)); \ break; \ case 0x58: \ out = P ^ (D & (S | P)); \ break; \ case 0x59: \ out = D ^ (P | ~S); \ break; \ case 0x5a: \ out = D ^ P; \ break; \ case 0x5b: \ out = D ^ (P | ~(S | D)); \ break; \ case 0x5c: \ out = D ^ (P | (S ^ D)); \ break; \ case 0x5d: \ out = ~(D & (P | ~S)); \ break; \ case 0x5e: \ out = D ^ (P | (S & ~D)); \ break; \ case 0x5f: \ out = ~(D & P); \ break; \ case 0x60: \ out = P & (D ^ S); \ break; \ case 0x61: \ out = ~(D ^ (S ^ (P | (D & S)))); \ break; \ case 0x62: \ out = D ^ (S & (P | D)); \ break; \ case 0x63: \ out = S ^ (D | ~P); \ break; \ case 0x64: \ out = S ^ (D & (P | S)); \ break; \ case 0x65: \ out = D ^ (S | ~P); \ break; \ case 0x66: \ out = D ^ S; \ break; \ case 0x67: \ out = S ^ (D | ~(P | S)); \ break; \ case 0x68: \ out = ~(D ^ (S ^ (P | ~(D | S)))); \ break; \ case 0x69: \ out = ~(P ^ (D ^ S)); \ break; \ case 0x6a: \ out = D ^ (P & S); \ break; \ case 0x6b: \ out = ~(P ^ (S ^ (D & (P | S)))); \ break; \ case 0x6c: \ out = S ^ (D & P); \ break; \ case 0x6d: \ out = ~(P ^ (D ^ (S & (P | D)))); \ break; \ case 0x6e: \ out = S ^ (D & (P | ~S)); \ break; \ case 0x6f: \ out = ~(P & ~(D ^ S)); \ break; \ case 0x70: \ out = P & ~(D & S); \ break; \ case 0x71: \ out = ~(S ^ ((S ^ D) & (P ^ D))); \ break; \ case 0x72: \ out = S ^ (D | (P ^ S)); \ break; \ case 0x73: \ out = ~(S & (D | ~P)); \ break; \ case 0x74: \ out = D ^ (S | (P ^ D)); \ break; \ case 0x75: \ out = ~(D & (S | ~P)); \ break; \ case 0x76: \ out = S ^ (D | (P & ~S)); \ break; \ case 0x77: \ out = ~(D & S); \ break; \ case 0x78: \ out = P ^ (D & S); \ break; \ case 0x79: \ out = ~(D ^ (S ^ (P & (D | S)))); \ break; \ case 0x7a: \ out = D ^ (P & (S | ~D)); \ break; \ case 0x7b: \ out = ~(S & ~(D ^ P)); \ break; \ case 0x7c: \ out = S ^ (P & (D | ~S)); \ break; \ case 0x7d: \ out = ~(D & ~(P ^ S)); \ break; \ case 0x7e: \ out = (S ^ P) | (D ^ S); \ break; \ case 0x7f: \ out = ~(D & (P & S)); \ break; \ case 0x80: \ out = D & (P & S); \ break; \ case 0x81: \ out = ~((S ^ P) | (D ^ S)); \ break; \ case 0x82: \ out = D & ~(P ^ S); \ break; \ case 0x83: \ out = ~(S ^ (P & (D | ~S))); \ break; \ case 0x84: \ out = S & ~(D ^ P); \ break; \ case 0x85: \ out = ~(P ^ (D & (S | ~P))); \ break; \ case 0x86: \ out = D ^ (S ^ (P & (D | S))); \ break; \ case 0x87: \ out = ~(P ^ (D & S)); \ break; \ case 0x88: \ out = D & S; \ break; \ case 0x89: \ out = ~(S ^ (D | (P & ~S))); \ break; \ case 0x8a: \ out = D & (S | ~P); \ break; \ case 0x8b: \ out = ~(D ^ (S | (P ^ D))); \ break; \ case 0x8c: \ out = S & (D | ~P); \ break; \ case 0x8d: \ out = ~(S ^ (D | (P ^ S))); \ break; \ case 0x8e: \ out = S ^ ((S ^ D) & (P ^ D)); \ break; \ case 0x8f: \ out = ~(P & ~(D & S)); \ break; \ case 0x90: \ out = P & ~(D ^ S); \ break; \ case 0x91: \ out = ~(S ^ (D & (P | ~S))); \ break; \ case 0x92: \ out = D ^ (P ^ (S & (D | P))); \ break; \ case 0x93: \ out = ~(S ^ (P & D)); \ break; \ case 0x94: \ out = P ^ (S ^ (D & (P | S))); \ break; \ case 0x95: \ out = ~(D ^ (P & S)); \ break; \ case 0x96: \ out = D ^ (P ^ S); \ break; \ case 0x97: \ out = P ^ (S ^ (D | ~(P | S))); \ break; \ case 0x98: \ out = ~(S ^ (D | ~(P | S))); \ break; \ case 0x99: \ out = ~(D ^ S); \ break; \ case 0x9a: \ out = D ^ (P & ~S); \ break; \ case 0x9b: \ out = ~(S ^ (D & (P | S))); \ break; \ case 0x9c: \ out = S ^ (P & ~D); \ break; \ case 0x9d: \ out = ~(D ^ (S & (P | D))); \ break; \ case 0x9e: \ out = D ^ (S ^ (P | (D & S))); \ break; \ case 0x9f: \ out = ~(P & (D ^ S)); \ break; \ case 0xa0: \ out = D & P; \ break; \ case 0xa1: \ out = ~(P ^ (D | (S & ~P))); \ break; \ case 0xa2: \ out = D & (P | ~S); \ break; \ case 0xa3: \ out = ~(D ^ (P | (S ^ D))); \ break; \ case 0xa4: \ out = ~(P ^ (D | ~(S | P))); \ break; \ case 0xa5: \ out = ~(P ^ D); \ break; \ case 0xa6: \ out = D ^ (S & ~P); \ break; \ case 0xa7: \ out = ~(P ^ (D & (S | P))); \ break; \ case 0xa8: \ out = D & (P | S); \ break; \ case 0xa9: \ out = ~(D ^ (P | S)); \ break; \ case 0xaa: \ out = D; \ break; \ case 0xab: \ out = D | ~(P | S); \ break; \ case 0xac: \ out = S ^ (P & (D ^ S)); \ break; \ case 0xad: \ out = ~(D ^ (P | (S & D))); \ break; \ case 0xae: \ out = D | (S & ~P); \ break; \ case 0xaf: \ out = D | ~P; \ break; \ case 0xb0: \ out = P & (D | ~S); \ break; \ case 0xb1: \ out = ~(P ^ (D | (S ^ P))); \ break; \ case 0xb2: \ out = S ^ ((S ^ P) | (D ^ S)); \ break; \ case 0xb3: \ out = ~(S & ~(D & P)); \ break; \ case 0xb4: \ out = P ^ (S & ~D); \ break; \ case 0xb5: \ out = ~(D ^ (P & (S | D))); \ break; \ case 0xb6: \ out = D ^ (P ^ (S | (D & P))); \ break; \ case 0xb7: \ out = ~(S & (D ^ P)); \ break; \ case 0xb8: \ out = P ^ (S & (D ^ P)); \ break; \ case 0xb9: \ out = ~(D ^ (S | (P & D))); \ break; \ case 0xba: \ out = D | (P & ~S); \ break; \ case 0xbb: \ out = D | ~S; \ break; \ case 0xbc: \ out = S ^ (P & ~(D & S)); \ break; \ case 0xbd: \ out = ~((S ^ D) & (P ^ D)); \ break; \ case 0xbe: \ out = D | (P ^ S); \ break; \ case 0xbf: \ out = D | ~(P & S); \ break; \ case 0xc0: \ out = P & S; \ break; \ case 0xc1: \ out = ~(S ^ (P | (D & ~S))); \ break; \ case 0xc2: \ out = ~(S ^ (P | ~(D | S))); \ break; \ case 0xc3: \ out = ~(P ^ S); \ break; \ case 0xc4: \ out = S & (P | ~D); \ break; \ case 0xc5: \ out = ~(S ^ (P | (D ^ S))); \ break; \ case 0xc6: \ out = S ^ (D & ~P); \ break; \ case 0xc7: \ out = ~(P ^ (S & (D | P))); \ break; \ case 0xc8: \ out = S & (D | P); \ break; \ case 0xc9: \ out = ~(S ^ (P | D)); \ break; \ case 0xca: \ out = D ^ (P & (S ^ D)); \ break; \ case 0xcb: \ out = ~(S ^ (P | (D & S))); \ break; \ case 0xcc: \ out = S; \ break; \ case 0xcd: \ out = S | ~(D | P); \ break; \ case 0xce: \ out = S | (D & ~P); \ break; \ case 0xcf: \ out = S | ~P; \ break; \ case 0xd0: \ out = P & (S | ~D); \ break; \ case 0xd1: \ out = ~(P ^ (S | (D ^ P))); \ break; \ case 0xd2: \ out = P ^ (D & ~S); \ break; \ case 0xd3: \ out = ~(S ^ (P & (D | S))); \ break; \ case 0xd4: \ out = S ^ ((S ^ P) & (P ^ D)); \ break; \ case 0xd5: \ out = ~(D & ~(P & S)); \ break; \ case 0xd6: \ out = P ^ (S ^ (D | (P & S))); \ break; \ case 0xd7: \ out = ~(D & (P ^ S)); \ break; \ case 0xd8: \ out = P ^ (D & (S ^ P)); \ break; \ case 0xd9: \ out = ~(S ^ (D | (P & S))); \ break; \ case 0xda: \ out = D ^ (P & ~(S & D)); \ break; \ case 0xdb: \ out = ~((S ^ P) & (D ^ S)); \ break; \ case 0xdc: \ out = S | (P & ~D); \ break; \ case 0xdd: \ out = S | ~D; \ break; \ case 0xde: \ out = S | (D ^ P); \ break; \ case 0xdf: \ out = S | ~(D & P); \ break; \ case 0xe0: \ out = P & (D | S); \ break; \ case 0xe1: \ out = ~(P ^ (D | S)); \ break; \ case 0xe2: \ out = D ^ (S & (P ^ D)); \ break; \ case 0xe3: \ out = ~(P ^ (S | (D & P))); \ break; \ case 0xe4: \ out = S ^ (D & (P ^ S)); \ break; \ case 0xe5: \ out = ~(P ^ (D | (S & P))); \ break; \ case 0xe6: \ out = S ^ (D & ~(P & S)); \ break; \ case 0xe7: \ out = ~((S ^ P) & (P ^ D)); \ break; \ case 0xe8: \ out = S ^ ((S ^ P) & (D ^ S)); \ break; \ case 0xe9: \ out = ~(D ^ (S ^ (P & ~(D & S)))); \ break; \ case 0xea: \ out = D | (P & S); \ break; \ case 0xeb: \ out = D | ~(P ^ S); \ break; \ case 0xec: \ out = S | (D & P); \ break; \ case 0xed: \ out = S | ~(D ^ P); \ break; \ case 0xee: \ out = D | S; \ break; \ case 0xef: \ out = S | (D | ~P); \ break; \ case 0xf0: \ out = P; \ break; \ case 0xf1: \ out = P | ~(D | S); \ break; \ case 0xf2: \ out = P | (D & ~S); \ break; \ case 0xf3: \ out = P | ~S; \ break; \ case 0xf4: \ out = P | (S & ~D); \ break; \ case 0xf5: \ out = P | ~D; \ break; \ case 0xf6: \ out = P | (D ^ S); \ break; \ case 0xf7: \ out = P | ~(D & S); \ break; \ case 0xf8: \ out = P | (D & S); \ break; \ case 0xf9: \ out = P | ~(D ^ S); \ break; \ case 0xfa: \ out = D | P; \ break; \ case 0xfb: \ out = D | (P | ~S); \ break; \ case 0xfc: \ out = P | S; \ break; \ case 0xfd: \ out = P | (S | ~D); \ break; \ case 0xfe: \ out = D | (P | S); \ break; \ case 0xff: \ out = ~0; \ break; \ } \ } #define ROPMIX \ { \ old_dest_dat = dest_dat; \ ROPMIX_READ(dest_dat, pat_dat, src_dat); \ out = (out & s3->accel.wrt_mask) | (old_dest_dat & ~s3->accel.wrt_mask); \ } #define WRITE(addr, dat) \ if ((s3->bpp == 0) && !s3->color_16bit) { \ svga->vram[dword_remap(svga, addr) & s3->vram_mask] = dat; \ svga->changedvram[(dword_remap(svga, addr) & s3->vram_mask) >> 12] = svga->monitor->mon_changeframecount; \ } else if ((s3->bpp == 1) || (s3->color_16bit && (svga->bpp < 24))) { \ vram_w[dword_remap_w(svga, addr) & (s3->vram_mask >> 1)] = dat; \ svga->changedvram[(dword_remap_w(svga, addr) & (s3->vram_mask >> 1)) >> 11] = svga->monitor->mon_changeframecount; \ } else if (s3->bpp == 2) { \ svga->vram[dword_remap(svga, addr) & s3->vram_mask] = dat; \ svga->changedvram[(dword_remap(svga, addr) & s3->vram_mask) >> 12] = svga->monitor->mon_changeframecount; \ } else if (s3->color_16bit && (svga->bpp == 24)) { \ vram_w[dword_remap_w(svga, addr) & (s3->vram_mask >> 1)] = dat; \ svga->changedvram[(dword_remap_w(svga, addr) & (s3->vram_mask >> 1)) >> 11] = svga->monitor->mon_changeframecount; \ } else { \ vram_l[dword_remap_l(svga, addr) & (s3->vram_mask >> 2)] = dat; \ svga->changedvram[(dword_remap_l(svga, addr) & (s3->vram_mask >> 2)) >> 10] = svga->monitor->mon_changeframecount; \ } static __inline void convert_to_rgb32(int idf, int is_yuv, uint32_t val, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *r2, uint8_t *g2, uint8_t *b2) { static double dr = 0.0; static double dg = 0.0; static double db = 0.0; static double dY1 = 0.0; static double dCr = 0.0; static double dY2 = 0.0; static double dCb = 0.0; static double dU = 0.0; static double dV = 0.0; switch (idf) { case 0: /* 8 bpp, RGB 3-3-2 */ dr = (double) ((val >> 5) & 0x07); dg = (double) ((val >> 2) & 0x07); db = (double) (val & 0x03); dr = (dr / 7.0) * 255.0; dg = (dg / 7.0) * 255.0; db = (db / 3.0) * 255.0; break; case 3: /* 32bpp, RGB 8-8-8 */ dr = (double) ((val >> 16) & 0xff); dg = (double) ((val >> 8) & 0xff); db = (double) (val & 0xff); break; case 4: /* YCbCr */ if (is_yuv) { dU = ((double) (val & 0xff)) - 128.0; dY1 = (double) ((val >> 8) & 0xff); dY1 = (298.0 * (dY1 - 16.0)) / 256.0; dV = ((double) ((val >> 16) & 0xff)) - 128.0; dY2 = (double) ((val >> 24) & 0xff); dY2 = (298.0 * (dY2 - 16.0)) / 256.0; dr = (309.0 * dV) / 256.0; dg = ((100.0 * dU) + (208.0 * dV)) / 256.0; db = (516.0 * dU) / 256.0; } else { dY1 = (double) (val & 0xff); dCr = ((double) ((val >> 8) & 0xff)) - 128.0; dY2 = (double) ((val >> 16) & 0xff); dCb = ((double) ((val >> 24) & 0xff)) - 128.0; dr = (359.0 * dCr) / 256.0; dg = ((88.0 * dCb) + (183.0 * dCr)) / 2560.0; db = (453.0 * dCr) / 256.0; } *r = (uint8_t) round(dY1 + dr); CLAMP(*r); *g = (uint8_t) round(dY1 - dg); CLAMP(*g); *b = (uint8_t) round(dY1 + db); CLAMP(*b); *r2 = (uint8_t) round(dY2 + dr); CLAMP(*r2); *g2 = (uint8_t) round(dY2 - dg); CLAMP(*g2); *b2 = (uint8_t) round(dY2 + db); CLAMP(*b2); return; case 5: /* 16bpp, raw */ case 7: /* 16bpp, RGB 5-6-5 */ dr = (double) ((val >> 11) & 0x1f); dg = (double) ((val >> 5) & 0x03f); db = (double) (val & 0x1f); dr = (dr / 31.0) * 255.0; dg = (dg / 63.0) * 255.0; db = (db / 31.0) * 255.0; break; case 6: /* 15bpp, RGB 5-5-5 */ dr = (double) ((val >> 10) & 0x1f); dg = (double) ((val >> 5) & 0x01f); db = (double) (val & 0x1f); dr = (dr / 31.0) * 255.0; dg = (dg / 31.0) * 255.0; db = (db / 31.0) * 255.0; break; default: break; } *r = (uint8_t) round(dr); *g = (uint8_t) round(dg); *b = (uint8_t) round(db); } static __inline void convert_from_rgb32(int idf, int odf, int is_yuv, uint32_t *val, uint8_t r, uint8_t g, uint8_t b, uint8_t r2, uint8_t g2, uint8_t b2) { static double dr = 0.0; static double dg = 0.0; static double db = 0.0; static double dr2 = 0.0; static double dg2 = 0.0; static double db2 = 0.0; static double dY1 = 0.0; static double dCr = 0.0; static double dY2 = 0.0; static double dCb = 0.0; static double dU = 0.0; static double dV = 0.0; dr = (double) r; dg = (double) g; db = (double) b; switch (odf) { case 0: /* 8 bpp, RGB 3-3-2 */ switch (idf) { case 3: *val = (((uint32_t) round(dr)) << 16) + (((uint32_t) round(dg)) << 8) + ((uint32_t) round(db)); break; case 5: case 7: dr = (dr / 255.0) * 31.0; dg = (dg / 255.0) * 63.0; db = (db / 255.0) * 31.0; *val = (((uint32_t) round(dr)) << 11) + (((uint32_t) round(dg)) << 5) + ((uint32_t) round(db)); break; case 6: dr = (dr / 255.0) * 31.0; dg = (dg / 255.0) * 31.0; db = (db / 255.0) * 31.0; *val = (((uint32_t) round(dr)) << 10) + (((uint32_t) round(dg)) << 5) + ((uint32_t) round(db)); break; case 0: default: dr = (dr / 255.0) * 7.0; dg = (dg / 255.0) * 7.0; db = (db / 255.0) * 3.0; *val = (((uint32_t) round(dr)) << 5) + (((uint32_t) round(dg)) << 2) + ((uint32_t) round(db)); break; } break; case 3: /* 32bpp, RGB 8-8-8 */ *val = (((uint32_t) round(dr)) << 16) + (((uint32_t) round(dg)) << 8) + ((uint32_t) round(db)); break; case 4: /* YCbCr */ dr2 = (double) r2; dg2 = (double) g2; db2 = (double) b2; if (is_yuv) { dU = ((113046.0 * dg2) - (71552.0 * dr2) - (69488.0 * db2)) / 28509.0; dV = ((3328.0 * dr2) + (800.0 * db2) - (4128.0 * dg2)) / 663.0; dY1 = dr - ((309 * dV) / 256.0); dY2 = dr2 - ((309 * dV) / 256.0); *val = ((uint32_t) round(dU)) + (((uint32_t) round(dY1)) << 8) + (((uint32_t) round(dV)) << 16) + (((uint32_t) round(dY2)) << 24); } else { dCr = ((128.0 * db2) - (128.0 * dr2)) / 47.0; dCb = ((128.0 * dr2) - (128.0 * dg2) - (271.0 * dCr)) / 44.0; dY1 = dr - ((359.0 * dCr) / 256.0); dY2 = dr2 - ((359.0 * dCr) / 256.0); *val = ((uint32_t) round(dY1)) + (((uint32_t) round(dCr)) << 8) + (((uint32_t) round(dY2)) << 16) + (((uint32_t) round(dCb)) << 24); } return; case 5: /* 16bpp, raw */ case 7: /* 16bpp, RGB 5-6-5 */ dr = (dr / 255.0) * 31.0; dg = (dg / 255.0) * 63.0; db = (db / 255.0) * 31.0; *val = (((uint32_t) round(dr)) << 11) + (((uint32_t) round(dg)) << 5) + ((uint32_t) round(db)); break; case 6: /* 15bpp, RGB 5-5-5 */ dr = (dr / 255.0) * 31.0; dg = (dg / 255.0) * 31.0; db = (db / 255.0) * 31.0; *val = (((uint32_t) round(dr)) << 10) + (((uint32_t) round(dg)) << 5) + ((uint32_t) round(db)); break; default: break; } } /*To Do: Dithering, color space conversion.*/ static void s3_visionx68_video_engine_op(uint32_t cpu_dat, s3_t *s3) { svga_t *svga = &s3->svga; int idf; int odf; int host; int is_yuv; uint32_t src; uint32_t dest = 0x00000000; uint8_t r = 0x00; uint8_t g = 0x00; uint8_t b = 0x00; uint8_t r2 = 0x00; uint8_t g2 = 0x00; uint8_t b2 = 0x00; uint16_t *vram_w = (uint16_t *) svga->vram; uint32_t *vram_l = (uint32_t *) svga->vram; uint32_t k2 = 0; uint32_t dda = 0; uint32_t diff = 0; int count = -1; idf = s3->videoengine.idf; odf = s3->videoengine.odf; is_yuv = s3->videoengine.yuv; host = s3->videoengine.host_data; k2 = s3->videoengine.k2 - 0x700; dda = s3->videoengine.dda_init_accumulator - 0xf00; diff = 0xff - k2; s3->videoengine.busy = 1; if (host) { if (idf == 0 && odf == 0) { if (s3->bpp == 0) count = 4; else if (s3->bpp == 1) count = 2; else count = 1; } else { if (idf == 0) count = 4; else if (idf == 3) count = 1; else count = 2; } } if (s3->videoengine.input == 1) { if (s3->videoengine.scale_down) { if (s3->bpp > 1) { s3->videoengine.sx = k2 - dda + diff; s3->videoengine.sx_backup = s3->videoengine.len - s3->videoengine.start; } else { s3->videoengine.sx = k2 - dda + diff - 1; s3->videoengine.sx_backup = s3->videoengine.len - s3->videoengine.start - 1; } s3->videoengine.sx_scale_inc = (double) (s3->videoengine.sx_backup >> 1); s3->videoengine.sx_scale_inc = s3->videoengine.sx_scale_inc / (double) (s3->videoengine.sx >> 1); } else { s3->videoengine.sx_scale = (double) (s3->videoengine.k1 - 2); s3->videoengine.sx_scale_dec = (s3->videoengine.sx_scale / (double) (s3->videoengine.len - s3->videoengine.start - 2)); if (s3->videoengine.sx_scale_dec >= 0.5) { s3->videoengine.sx_scale++; } } if (s3->bpp == 0) { s3->videoengine.dest = s3->videoengine.dest_base + s3->width; s3->videoengine.src = s3->videoengine.src_base + s3->width; } else if (s3->bpp == 1) { s3->videoengine.dest = (s3->videoengine.dest_base >> 1) + s3->width; s3->videoengine.src = (s3->videoengine.src_base >> 1) + s3->width; } else { s3->videoengine.dest = (s3->videoengine.dest_base >> 2) + s3->width; s3->videoengine.src = (s3->videoengine.src_base >> 2) + s3->width; } s3->videoengine.input = 2; s3->videoengine.cx = 0.0; s3->videoengine.dx = 0.0; } while (count) { if (host) { /*Source data is CPU*/ src = cpu_dat; } else { /*Source data is display memory*/ READ(s3->videoengine.src + lround(s3->videoengine.cx), src); } convert_to_rgb32(idf, is_yuv, src, &r, &g, &b, &r2, &g2, &b2); convert_from_rgb32(idf, odf, is_yuv, &dest, r, g, b, r2, g2, b2); WRITE(s3->videoengine.dest + lround(s3->videoengine.dx), dest); if (s3->videoengine.scale_down) { /*Data shrink*/ s3->videoengine.dx += s3->videoengine.sx_scale_inc; if (!host) s3->videoengine.cx += s3->videoengine.sx_scale_inc; s3->videoengine.sx--; if (host) { if (s3->bpp == 0) { cpu_dat >>= 8; } else { cpu_dat >>= 16; } count--; } if (s3->videoengine.sx < 0) { if (s3->bpp > 1) { s3->videoengine.sx = k2 - dda + diff; s3->videoengine.sx_backup = s3->videoengine.len - s3->videoengine.start; } else { s3->videoengine.sx = k2 - dda + diff - 1; s3->videoengine.sx_backup = s3->videoengine.len - s3->videoengine.start - 1; } s3->videoengine.sx_scale_inc = (double) (s3->videoengine.sx_backup >> 1); s3->videoengine.sx_scale_inc = s3->videoengine.sx_scale_inc / (double) (s3->videoengine.sx >> 1); s3->videoengine.cx = 0.0; s3->videoengine.dx = 0.0; if (s3->bpp == 0) { s3->videoengine.dest = s3->videoengine.dest_base + s3->width; s3->videoengine.src = s3->videoengine.src_base + s3->width; } else if (s3->bpp == 1) { s3->videoengine.dest = (s3->videoengine.dest_base >> 1) + s3->width; s3->videoengine.src = (s3->videoengine.src_base >> 1) + s3->width; } else { s3->videoengine.dest = (s3->videoengine.dest_base >> 2) + s3->width; s3->videoengine.src = (s3->videoengine.src_base >> 2) + s3->width; } if (s3->videoengine.input >= 1) { s3->videoengine.busy = 0; return; } } } else { /*Data stretch*/ s3->videoengine.dx++; s3->videoengine.sx_scale -= s3->videoengine.sx_scale_dec; s3->videoengine.sx_scale_backup = (s3->videoengine.sx_scale - s3->videoengine.sx_scale_dec); s3->videoengine.sx = lround(s3->videoengine.sx_scale); s3->videoengine.sx_scale_int = lround(s3->videoengine.sx_scale_backup); if (s3->videoengine.sx > s3->videoengine.sx_scale_int) { if (host) { if (s3->bpp == 0) cpu_dat >>= 8; else cpu_dat >>= 16; count--; } else { s3->videoengine.cx++; } } if (s3->videoengine.sx < 0) { s3->videoengine.sx_scale = (double) (s3->videoengine.k1 - 2); s3->videoengine.sx_scale_dec = (s3->videoengine.sx_scale / (double) (s3->videoengine.len - s3->videoengine.start - 2)); if (s3->videoengine.sx_scale_dec >= 0.5) { s3->videoengine.sx_scale++; } s3->videoengine.cx = 0.0; s3->videoengine.dx = 0.0; if (s3->bpp == 0) { s3->videoengine.dest = s3->videoengine.dest_base + s3->width; s3->videoengine.src = s3->videoengine.src_base + s3->width; } else if (s3->bpp == 1) { s3->videoengine.dest = (s3->videoengine.dest_base >> 1) + s3->width; s3->videoengine.src = (s3->videoengine.src_base >> 1) + s3->width; } else { s3->videoengine.dest = (s3->videoengine.dest_base >> 2) + s3->width; s3->videoengine.src = (s3->videoengine.src_base >> 2) + s3->width; } if (s3->videoengine.input >= 1) { s3->videoengine.busy = 0; return; } } } } } void s3_911_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, void *priv) { s3_t *s3 = (s3_t *)priv; svga_t *svga = &s3->svga; uint32_t src_dat = 0; uint32_t dest_dat; uint32_t old_dest_dat; int frgd_mix; int bkgd_mix; int clip_t = s3->accel.multifunc[1] & 0xfff; int clip_l = s3->accel.multifunc[2] & 0xfff; int clip_b = s3->accel.multifunc[3] & 0xfff; int clip_r = s3->accel.multifunc[4] & 0xfff; int vram_mask = (s3->accel.multifunc[0xa] & 0xc0) == 0xc0; uint32_t mix_mask = (s3->accel.cmd & 0x200) ? 0x8000 : 0x80; uint16_t *vram_w = (uint16_t *) svga->vram; uint32_t *vram_l = (uint32_t *) svga->vram; uint32_t rd_mask = s3->accel.rd_mask; uint32_t wrt_mask = s3->accel.wrt_mask; uint32_t frgd_color = s3->accel.frgd_color; uint32_t bkgd_color = s3->accel.bkgd_color; int cmd = s3->accel.cmd >> 13; if ((s3->accel.cmd & 0x100) && (s3_cpu_src(s3) || (s3_cpu_dest(s3))) && (!cpu_input || (s3_enable_fifo(s3) == 0))) s3->force_busy = 1; if (cpu_input && (((s3->accel.multifunc[0xa] & 0xc0) != 0x80) || (!(s3->accel.cmd & 2)))) { if (s3->color_16bit) { if (count > 1) count >>= 1; } } if (s3->color_16bit) rd_mask &= 0xffff; else rd_mask &= 0xff; /*Bit 4 of the Command register is the draw yes bit, which enables writing to memory/reading from memory when enabled. When this bit is disabled, no writing to memory/reading from memory is allowed. (This bit is almost meaningless on the NOP command)*/ switch (cmd) { case 0: /*NOP (Short Stroke Vectors)*/ if (s3->accel.ssv_state == 0) break; frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; if (s3->accel.cmd & 8) { /*Radial*/ while (count-- && s3->accel.ssv_len >= 0) { if ((s3->accel.cx & 0xfff) >= clip_l && (s3->accel.cx & 0xfff) <= clip_r && (s3->accel.cy & 0xfff) >= clip_t && (s3->accel.cy & 0xfff) <= clip_b) { switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = bkgd_color; break; case 1: src_dat = frgd_color; break; case 2: src_dat = cpu_dat; break; case 3: src_dat = 0; break; default: break; } READ((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); MIX if (s3->accel.ssv_draw) { WRITE((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); } } mix_dat <<= 1; mix_dat |= 1; if (s3->bpp == 0) cpu_dat >>= 8; else cpu_dat >>= 16; if (!s3->accel.ssv_len) break; switch (s3->accel.ssv_dir & 0xe0) { case 0x00: s3->accel.cx++; break; case 0x20: s3->accel.cx++; s3->accel.cy--; break; case 0x40: s3->accel.cy--; break; case 0x60: s3->accel.cx--; s3->accel.cy--; break; case 0x80: s3->accel.cx--; break; case 0xa0: s3->accel.cx--; s3->accel.cy++; break; case 0xc0: s3->accel.cy++; break; case 0xe0: s3->accel.cx++; s3->accel.cy++; break; default: break; } s3->accel.ssv_len--; s3->accel.cx &= 0xfff; s3->accel.cy &= 0xfff; } s3->accel.cur_x = s3->accel.cx; s3->accel.cur_y = s3->accel.cy; } break; case 1: /*Draw line*/ if (!cpu_input) { s3->accel.cx = s3->accel.cur_x & 0xfff; s3->accel.cy = s3->accel.cur_y & 0xfff; s3->accel.sy = s3->accel.maj_axis_pcnt & 0x7ff; if (s3->color_16bit && (svga->bpp < 24)) { if (s3->accel.wrt_mask != 0xffff) { if (s3->accel.cur_x & 0x400) { s3->accel.color_16bit_check = 0; s3->accel.minus = 0x400; } else { s3->accel.color_16bit_check = 1; s3->accel.minus = 0; } } else { if (s3->accel.cur_x & 0x400) s3->accel.color_16bit_check = 1; else s3->accel.color_16bit_check = 0; s3->accel.minus = 0; } } else { s3->accel.color_16bit_check = 0; s3->accel.minus = 0; } if (s3_cpu_src(s3)) return; /*Wait for data from CPU*/ } frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; if (s3->accel.cmd & 8) { /*Radial*/ if (s3->color_16bit && (svga->bpp < 24)) { if (s3->accel.color_16bit_check) return; if (s3->accel.wrt_mask != 0xffff) wrt_mask = (s3->accel.wrt_mask_actual[0] | (s3->accel.wrt_mask_actual[1] << 8)); } while (count-- && s3->accel.sy >= 0) { if ((s3->accel.cx & 0xfff) >= clip_l && (s3->accel.cx & 0xfff) <= clip_r && (s3->accel.cy & 0xfff) >= clip_t && (s3->accel.cy & 0xfff) <= clip_b) { switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = bkgd_color; if (s3->color_16bit && (svga->bpp < 24)) src_dat = s3->accel.bkgd_color_actual[0] | (s3->accel.bkgd_color_actual[1] << 8); break; case 1: src_dat = frgd_color; if (s3->color_16bit && (svga->bpp < 24)) src_dat = s3->accel.frgd_color_actual[0] | (s3->accel.frgd_color_actual[1] << 8); break; case 2: src_dat = cpu_dat; break; case 3: src_dat = 0; break; default: break; } READ((s3->accel.cy * s3->width) + s3->accel.cx - s3->accel.minus, dest_dat); MIX WRITE((s3->accel.cy * s3->width) + s3->accel.cx - s3->accel.minus, dest_dat); } mix_dat <<= 1; mix_dat |= 1; if ((s3->bpp == 0) && !s3->color_16bit) cpu_dat >>= 8; else cpu_dat >>= 16; if (!s3->accel.sy) break; switch (s3->accel.cmd & 0xe0) { case 0x00: s3->accel.cx++; break; case 0x20: s3->accel.cx++; s3->accel.cy--; break; case 0x40: s3->accel.cy--; break; case 0x60: s3->accel.cx--; s3->accel.cy--; break; case 0x80: s3->accel.cx--; break; case 0xa0: s3->accel.cx--; s3->accel.cy++; break; case 0xc0: s3->accel.cy++; break; case 0xe0: s3->accel.cx++; s3->accel.cy++; break; default: break; } s3->accel.sy--; s3->accel.cx &= 0xfff; s3->accel.cy &= 0xfff; } s3->accel.cur_x = s3->accel.cx; s3->accel.cur_y = s3->accel.cy; } else { /*Bresenham*/ if (s3->accel.b2e8_pix && s3_cpu_src(s3) && (count == 16)) { /*Stupid undocumented 0xB2E8 on 911/924*/ count = s3->accel.maj_axis_pcnt + 1; s3->accel.temp_cnt = 16; } if (s3->color_16bit && (svga->bpp < 24)) { if (!s3->accel.b2e8_pix) { if (!s3->accel.color_16bit_check) wrt_mask = (s3->accel.wrt_mask_actual[0] | (s3->accel.wrt_mask_actual[1] << 8)); else return; } } s3_log("CMD=%04x, curx=%d, lwrtmask=%04x, actual wrtmask=%04x, frgdmix=%d, " "bkgdmix=%d, input=%d, cnt=%d.\n", s3->accel.cmd, s3->accel.cur_x, wrt_mask, s3->accel.wrt_mask, frgd_mix, bkgd_mix, cpu_input, count); while (count-- && s3->accel.sy >= 0) { if (s3->accel.b2e8_pix && s3_cpu_src(s3) && !s3->accel.temp_cnt) { mix_dat >>= 16; s3->accel.temp_cnt = 16; } if ((s3->accel.cx & 0xfff) >= clip_l && (s3->accel.cx & 0xfff) <= clip_r && (s3->accel.cy & 0xfff) >= clip_t && (s3->accel.cy & 0xfff) <= clip_b) { switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = bkgd_color; if (s3->color_16bit && (svga->bpp < 24) && !s3->accel.b2e8_pix) { if (!s3->accel.color_16bit_check) src_dat = s3->accel.bkgd_color_actual[0] | (s3->accel.bkgd_color_actual[1] << 8); } break; case 1: src_dat = frgd_color; if (s3->color_16bit && (svga->bpp < 24) && !s3->accel.b2e8_pix) { if (!s3->accel.color_16bit_check) src_dat = s3->accel.frgd_color_actual[0] | (s3->accel.frgd_color_actual[1] << 8); } break; case 2: src_dat = cpu_dat; break; case 3: src_dat = 0; break; default: break; } READ((s3->accel.cy * s3->width) + s3->accel.cx - s3->accel.minus, dest_dat); MIX if (s3->accel.cmd & 0x10) { WRITE((s3->accel.cy * s3->width) + s3->accel.cx - s3->accel.minus, dest_dat); } } if (s3->accel.b2e8_pix && s3_cpu_src(s3)) { if (s3->accel.temp_cnt > 0) { s3->accel.temp_cnt--; mix_dat <<= 1; mix_dat |= 1; } } else { mix_dat <<= 1; mix_dat |= 1; } if (s3->color_16bit) cpu_dat >>= 16; else cpu_dat >>= 8; if (!s3->accel.sy) break; if (s3->accel.cmd & 0x40) { if (s3->accel.cmd & 0x80) s3->accel.cy++; else s3->accel.cy--; if (s3->accel.err_term >= 0) { s3->accel.err_term += s3->accel.destx_distp; if (s3->accel.cmd & 0x20) s3->accel.cx++; else s3->accel.cx--; } else s3->accel.err_term += s3->accel.desty_axstp; } else { if (s3->accel.cmd & 0x20) s3->accel.cx++; else s3->accel.cx--; if (s3->accel.err_term >= 0) { s3->accel.err_term += s3->accel.destx_distp; if (s3->accel.cmd & 0x80) s3->accel.cy++; else s3->accel.cy--; } else s3->accel.err_term += s3->accel.desty_axstp; } s3->accel.sy--; s3->accel.cx &= 0xfff; s3->accel.cy &= 0xfff; } s3->accel.cur_x = s3->accel.cx; s3->accel.cur_y = s3->accel.cy; } break; case 2: /*Rectangle fill*/ if (!cpu_input) { /*!cpu_input is trigger to start operation*/ s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; s3->accel.sy = s3->accel.multifunc[0] & 0xfff; s3->accel.cx = s3->accel.cur_x & 0xfff; s3->accel.cy = s3->accel.cur_y & 0xfff; s3->accel.pix_trans_x_count = 0; s3->accel.dest = s3->accel.cy * s3->width; if (s3->color_16bit && (svga->bpp < 24)) { if (s3->accel.cur_x & 0x400) { s3->accel.color_16bit_check = 0; s3->accel.minus = 0x400; } else { s3->accel.color_16bit_check = 1; s3->accel.minus = 0; } if (s3->accel.color_16bit_check) { if (((s3->accel.multifunc[0xa] & 0xc0) == 0x00) && !(s3->accel.cmd & 2)) s3->accel.color_16bit_check_rectfill = !!s3_cpu_src(s3); else s3->accel.color_16bit_check_rectfill = 0; } if (s3->accel.color_16bit_check_rectfill) { if (s3->accel.color_16bit_check) { s3->accel.pix_trans_ptr = (uint8_t *) calloc(1, (s3->accel.sx + 1) << 1); s3->accel.pix_trans_ptr_cnt = (s3->accel.sx + 1) << 1; } } else s3->accel.pix_trans_x_count = 0; } else { s3->accel.pix_trans_x_count = 0; s3->accel.color_16bit_check = 0; s3->accel.color_16bit_check_rectfill = 0; s3->accel.minus = 0; } if (s3_cpu_src(s3)) { s3->data_available = 0; return; /*Wait for data from CPU*/ } else if (s3_cpu_dest(s3)) { s3->data_available = 1; return; } } frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; if (s3->accel.b2e8_pix && s3_cpu_src(s3) && count == 16) { /*Stupid undocumented 0xB2E8 on 911/924*/ count = s3->accel.maj_axis_pcnt + 1; s3->accel.temp_cnt = 16; } if (s3->color_16bit && (svga->bpp < 24)) { if (!s3->accel.b2e8_pix) { if (!s3->accel.color_16bit_check) { wrt_mask = (s3->accel.wrt_mask_actual[0] | (s3->accel.wrt_mask_actual[1] << 8)); } else if (s3->accel.color_16bit_check && (s3->accel.cmd == 0x40f3)) return; } } while (count-- && s3->accel.sy >= 0) { if (s3->accel.b2e8_pix && s3_cpu_src(s3) && !s3->accel.temp_cnt) { mix_dat >>= 16; s3->accel.temp_cnt = 16; } if ((s3->accel.cx & 0xfff) >= clip_l && (s3->accel.cx & 0xfff) <= clip_r && (s3->accel.cy & 0xfff) >= clip_t && (s3->accel.cy & 0xfff) <= clip_b) { if (s3_cpu_dest(s3) && ((s3->accel.multifunc[0xa] & 0xc0) == 0x00)) { mix_dat = mix_mask; /* Mix data = forced to foreground register. */ } else if (s3_cpu_dest(s3) && vram_mask) { /* Mix data = current video memory value. */ READ(s3->accel.dest + s3->accel.cx - s3->accel.minus, mix_dat); mix_dat = ((mix_dat & rd_mask) == rd_mask); mix_dat = mix_dat ? mix_mask : 0; } if (s3_cpu_dest(s3)) { READ(s3->accel.dest + s3->accel.cx - s3->accel.minus, src_dat); if (vram_mask) src_dat = ((src_dat & rd_mask) == rd_mask); } else { switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = bkgd_color; if (s3->color_16bit && (svga->bpp < 24) && !s3->accel.b2e8_pix && (s3->accel.cmd != 0x41b3)) { if (!s3->accel.color_16bit_check) src_dat = s3->accel.bkgd_color_actual[0] | (s3->accel.bkgd_color_actual[1] << 8); } break; case 1: src_dat = frgd_color; if (s3->color_16bit && (svga->bpp < 24) && !s3->accel.b2e8_pix && (s3->accel.cmd != 0x41b3)) { if (!s3->accel.color_16bit_check) src_dat = s3->accel.frgd_color_actual[0] | (s3->accel.frgd_color_actual[1] << 8); } break; case 2: src_dat = cpu_dat; break; case 3: src_dat = 0; break; default: break; } } READ(s3->accel.dest + s3->accel.cx - s3->accel.minus, dest_dat); MIX if (s3->accel.cmd & 0x10) { WRITE(s3->accel.dest + s3->accel.cx - s3->accel.minus, dest_dat); } } if (s3->accel.b2e8_pix && s3_cpu_src(s3)) { if (s3->accel.temp_cnt > 0) { s3->accel.temp_cnt--; mix_dat <<= 1; mix_dat |= 1; } } else { mix_dat <<= 1; mix_dat |= 1; } if (s3->color_16bit) cpu_dat >>= 16; else cpu_dat >>= 8; if (s3->accel.cmd & 0x20) s3->accel.cx++; else s3->accel.cx--; s3->accel.cx &= 0xfff; s3->accel.sx--; if (s3->accel.sx < 0) { if (s3->accel.cmd & 0x20) s3->accel.cx -= (s3->accel.maj_axis_pcnt & 0xfff) + 1; else s3->accel.cx += (s3->accel.maj_axis_pcnt & 0xfff) + 1; s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; if (s3->accel.cmd & 0x80) s3->accel.cy++; else s3->accel.cy--; s3->accel.cy &= 0xfff; s3->accel.dest = s3->accel.cy * s3->width; s3->accel.sy--; if (cpu_input) { if (s3->accel.b2e8_pix) { s3->accel.cur_x = s3->accel.cx; s3->accel.cur_y = s3->accel.cy; } return; } if (s3->accel.sy < 0) { s3->accel.cur_x = s3->accel.cx; s3->accel.cur_y = s3->accel.cy; return; } } } break; case 6: /*BitBlt*/ if (!cpu_input) { /*!cpu_input is trigger to start operation*/ s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; s3->accel.sy = s3->accel.multifunc[0] & 0xfff; s3->accel.dx = s3->accel.destx_distp & 0xfff; s3->accel.dy = s3->accel.desty_axstp & 0xfff; s3->accel.cx = s3->accel.cur_x & 0xfff; s3->accel.cy = s3->accel.cur_y & 0xfff; s3->accel.src = s3->accel.cy * s3->width; s3->accel.dest = s3->accel.dy * s3->width; if (s3->color_16bit && (svga->bpp < 24)) { if (s3->accel.destx_distp & 0x400) { s3->accel.color_16bit_check = 0; s3->accel.minus = 0x400; } else { s3->accel.color_16bit_check = 1; s3->accel.minus = 0; } s3->accel.srcminus = 0x400; } else { s3->accel.color_16bit_check = 0; s3->accel.minus = 0; s3->accel.srcminus = 0; } } if ((s3->accel.cmd & 0x100) && !cpu_input) { return; /*Wait for data from CPU*/ } frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; if (s3->color_16bit && (svga->bpp < 24)) { if (!s3->accel.color_16bit_check) wrt_mask = (s3->accel.wrt_mask_actual[0] | (s3->accel.wrt_mask_actual[1] << 8)); else return; } if (!cpu_input && (frgd_mix == 3) && !vram_mask && ((s3->accel.cmd & 0xa0) == 0xa0) && ((s3->accel.frgd_mix & 0xf) == 7) && ((s3->accel.bkgd_mix & 0xf) == 7)) { while (1) { if ((s3->accel.dx & 0xfff) >= clip_l && (s3->accel.dx & 0xfff) <= clip_r && (s3->accel.dy & 0xfff) >= clip_t && (s3->accel.dy & 0xfff) <= clip_b) { READ(s3->accel.src + s3->accel.cx - s3->accel.srcminus, src_dat); READ(s3->accel.dest + s3->accel.dx - s3->accel.minus, dest_dat); dest_dat = (src_dat & wrt_mask) | (dest_dat & ~wrt_mask); WRITE(s3->accel.dest + s3->accel.dx - s3->accel.minus, dest_dat); } s3->accel.cx++; s3->accel.dx++; s3->accel.sx--; s3->accel.dx &= 0xfff; if (s3->accel.sx < 0) { s3->accel.cx -= (s3->accel.maj_axis_pcnt & 0xfff) + 1; s3->accel.dx -= (s3->accel.maj_axis_pcnt & 0xfff) + 1; s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; s3->accel.cy++; s3->accel.dy++; s3->accel.dy &= 0xfff; s3->accel.src = s3->accel.cy * s3->width; s3->accel.dest = s3->accel.dy * s3->width; s3->accel.sy--; if (s3->accel.sy < 0) { /*It's evident that this is a clear undocumented difference compared to later chips, per what NT 3.5+ does to DX/DY.*/ s3->accel.destx_distp = s3->accel.dx; s3->accel.desty_axstp = s3->accel.dy; return; } } } } else { while (count-- && s3->accel.sy >= 0) { if ((s3->accel.dx & 0xfff) >= clip_l && (s3->accel.dx & 0xfff) <= clip_r && ((s3->accel.dy & 0xfff) >= clip_t && (s3->accel.dy & 0xfff) <= clip_b)) { if (vram_mask && (s3->accel.cmd & 0x10)) { READ(s3->accel.src + s3->accel.cx - s3->accel.srcminus, mix_dat); mix_dat = ((mix_dat & rd_mask) == rd_mask); mix_dat = mix_dat ? mix_mask : 0; } switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = bkgd_color; if (s3->color_16bit && (svga->bpp < 24)) { if (!s3->accel.color_16bit_check) src_dat = s3->accel.bkgd_color_actual[0] | (s3->accel.bkgd_color_actual[1] << 8); } break; case 1: src_dat = frgd_color; if (s3->color_16bit && (svga->bpp < 24)) { if (!s3->accel.color_16bit_check) src_dat = s3->accel.frgd_color_actual[0] | (s3->accel.frgd_color_actual[1] << 8); } break; case 2: src_dat = cpu_dat; break; case 3: READ(s3->accel.src + s3->accel.cx - s3->accel.srcminus, src_dat); if (vram_mask && (s3->accel.cmd & 0x10)) src_dat = ((src_dat & rd_mask) == rd_mask); break; default: break; } READ(s3->accel.dest + s3->accel.dx - s3->accel.minus, dest_dat); MIX if ((!(s3->accel.cmd & 0x10) && vram_mask) || (s3->accel.cmd & 0x10)) { WRITE(s3->accel.dest + s3->accel.dx - s3->accel.minus, dest_dat); } } mix_dat <<= 1; mix_dat |= 1; if (s3->color_16bit) cpu_dat >>= 16; else cpu_dat >>= 8; if (s3->accel.cmd & 0x20) { s3->accel.cx++; s3->accel.dx++; } else { s3->accel.cx--; s3->accel.dx--; } s3->accel.dx &= 0xfff; s3->accel.sx--; if (s3->accel.sx < 0) { if (s3->accel.cmd & 0x20) { s3->accel.cx -= ((s3->accel.maj_axis_pcnt & 0xfff) + 1); s3->accel.dx -= ((s3->accel.maj_axis_pcnt & 0xfff) + 1); } else { s3->accel.cx += ((s3->accel.maj_axis_pcnt & 0xfff) + 1); s3->accel.dx += ((s3->accel.maj_axis_pcnt & 0xfff) + 1); } s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; if (s3->accel.cmd & 0x80) { s3->accel.cy++; s3->accel.dy++; } else { s3->accel.cy--; s3->accel.dy--; } s3->accel.dy &= 0xfff; s3->accel.src = s3->accel.cy * s3->width; s3->accel.dest = s3->accel.dy * s3->width; s3->accel.sy--; if (cpu_input) return; if (s3->accel.sy < 0) { /*It's evident that this is a clear undocumented difference compared to later chips, per what NT 3.5+ does to DX/DY.*/ s3->accel.destx_distp = s3->accel.dx; s3->accel.desty_axstp = s3->accel.dy; return; } } } } break; default: break; } } void s3_short_stroke_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_t *s3, uint8_t ssv) { if (!cpu_input) { s3->accel.ssv_len = ssv & 0x0f; s3->accel.ssv_dir = ssv & 0xe0; s3->accel.ssv_draw = ssv & 0x10; if (s3_cpu_src(s3)) { return; /*Wait for data from CPU*/ } } s3->accel_start(count, cpu_input, mix_dat, cpu_dat, s3); } void s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, void *priv) { s3_t *s3 = (s3_t *)priv; svga_t *svga = &s3->svga; uint32_t src_dat = 0; uint32_t dest_dat; uint32_t old_dest_dat; uint32_t out; uint32_t pat_dat = 0; int frgd_mix; int bkgd_mix; int clip_t = s3->accel.multifunc[1] & 0xfff; int clip_l = s3->accel.multifunc[2] & 0xfff; int clip_b = s3->accel.multifunc[3] & 0xfff; int clip_r = s3->accel.multifunc[4] & 0xfff; int vram_mask = (s3->accel.multifunc[0xa] & 0xc0) == 0xc0; uint32_t mix_mask = 0; uint16_t *vram_w = (uint16_t *) svga->vram; uint32_t *vram_l = (uint32_t *) svga->vram; uint32_t compare = s3->accel.color_cmp; uint8_t rop = s3->accel.ropmix & 0xff; int compare_mode = (s3->accel.multifunc[0xe] >> 7) & 3; uint32_t rd_mask = s3->accel.rd_mask; uint32_t wrt_mask = s3->accel.wrt_mask; uint32_t frgd_color = s3->accel.frgd_color; uint32_t bkgd_color = s3->accel.bkgd_color; int cmd = s3->accel.cmd >> 13; uint32_t srcbase; uint32_t dstbase; s3->accel.srcminus = 0; s3->accel.minus = 0; if ((s3->chip >= S3_TRIO64 || s3->chip == S3_VISION968 || s3->chip == S3_VISION868) && (s3->accel.cmd & (1 << 11))) cmd |= 8; // SRC-BASE/DST-BASE if ((s3->accel.multifunc[0xd] >> 4) & 7) srcbase = 0x100000 * ((s3->accel.multifunc[0xd] >> 4) & 3); else srcbase = 0x100000 * ((s3->accel.multifunc[0xe] >> 2) & 3); if ((s3->accel.multifunc[0xd] >> 0) & 7) dstbase = 0x100000 * ((s3->accel.multifunc[0xd] >> 0) & 3); else dstbase = 0x100000 * ((s3->accel.multifunc[0xe] >> 0) & 3); if ((s3->bpp == 1) || s3->color_16bit) { srcbase >>= 1; dstbase >>= 1; } else if (s3->bpp == 3) { srcbase >>= 2; dstbase >>= 2; } if ((s3->accel.cmd & 0x100) && (s3_cpu_src(s3) || (s3_cpu_dest(s3))) && (!cpu_input || (s3_enable_fifo(s3) == 0))) s3->force_busy = 1; if (!cpu_input) s3->accel.dat_count = 0; if (cpu_input && (((s3->accel.multifunc[0xa] & 0xc0) != 0x80) || (!(s3->accel.cmd & 2)))) { if ((s3->bpp == 3) && (count == 2)) { if (s3->accel.dat_count) { cpu_dat = ((cpu_dat & 0xffff) << 16) | s3->accel.dat_buf; count = 4; s3->accel.dat_count = 0; } else { s3->accel.dat_buf = cpu_dat & 0xffff; s3->accel.dat_count = 1; } } if ((s3->bpp == 1) || s3->color_16bit) count >>= 1; else if (s3->bpp == 3) count >>= 2; } if ((s3->bpp == 0) && !s3->color_16bit) rd_mask &= 0xff; else if ((s3->bpp == 1) || s3->color_16bit) rd_mask &= 0xffff; if (s3->bpp == 0) compare &= 0xff; else if (s3->bpp == 1) compare &= 0xffff; switch (s3->accel.cmd & 0x600) { case 0x000: mix_mask = 0x80; break; case 0x200: mix_mask = 0x8000; break; case 0x400: mix_mask = 0x80000000; break; case 0x600: mix_mask = (s3->chip == S3_TRIO32 || s3->chip >= S3_TRIO64V || s3->chip == S3_VISION968 || s3->chip == S3_VISION868) ? 0x80 : 0x80000000; break; default: break; } /*Bit 4 of the Command register is the draw yes bit, which enables writing to memory/reading from memory when enabled. When this bit is disabled, no writing to memory/reading from memory is allowed. (This bit is almost meaningless on the NOP command)*/ switch (cmd) { case 0: /*NOP (Short Stroke Vectors)*/ if (s3->accel.ssv_state == 0) break; frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; if (s3->accel.cmd & 8) /*Radial*/ { while (count-- && s3->accel.ssv_len >= 0) { if ((s3->accel.cx & 0xfff) >= clip_l && (s3->accel.cx & 0xfff) <= clip_r && (s3->accel.cy & 0xfff) >= clip_t && (s3->accel.cy & 0xfff) <= clip_b) { switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = bkgd_color; break; case 1: src_dat = frgd_color; break; case 2: src_dat = cpu_dat; break; case 3: src_dat = 0; break; default: break; } if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || compare_mode < 2) { READ((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); MIX if (s3->accel.ssv_draw) { WRITE((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); } } } mix_dat <<= 1; mix_dat |= 1; if (s3->bpp == 0 && !s3->color_16bit) cpu_dat >>= 8; else cpu_dat >>= 16; if (!s3->accel.ssv_len) break; switch (s3->accel.ssv_dir & 0xe0) { case 0x00: s3->accel.cx++; break; case 0x20: s3->accel.cx++; s3->accel.cy--; break; case 0x40: s3->accel.cy--; break; case 0x60: s3->accel.cx--; s3->accel.cy--; break; case 0x80: s3->accel.cx--; break; case 0xa0: s3->accel.cx--; s3->accel.cy++; break; case 0xc0: s3->accel.cy++; break; case 0xe0: s3->accel.cx++; s3->accel.cy++; break; default: break; } s3->accel.ssv_len--; s3->accel.cx &= 0xfff; s3->accel.cy &= 0xfff; } s3->accel.cur_x = s3->accel.cx & 0xfff; s3->accel.cur_y = s3->accel.cy & 0xfff; } break; case 1: /*Draw line*/ if (!cpu_input) { s3->accel.cx = s3->accel.cur_x & 0xfff; s3->accel.cy = s3->accel.cur_y & 0xfff; s3->accel.sy = s3->accel.maj_axis_pcnt; if (s3_cpu_src(s3)) return; /*Wait for data from CPU*/ } frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; if (s3->accel.cmd & 8) { /*Radial*/ while (count-- && s3->accel.sy >= 0) { if ((s3->accel.cx & 0xfff) >= clip_l && (s3->accel.cx & 0xfff) <= clip_r && (s3->accel.cy & 0xfff) >= clip_t && (s3->accel.cy & 0xfff) <= clip_b) { switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = bkgd_color; break; case 1: src_dat = frgd_color; break; case 2: src_dat = cpu_dat; break; case 3: src_dat = 0; break; default: break; } if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || compare_mode < 2) { READ((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); MIX WRITE((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); } } mix_dat <<= 1; mix_dat |= 1; if ((s3->bpp == 0) && !s3->color_16bit) cpu_dat >>= 8; else cpu_dat >>= 16; if (!s3->accel.sy) break; switch (s3->accel.cmd & 0xe0) { case 0x00: s3->accel.cx++; break; case 0x20: s3->accel.cx++; s3->accel.cy--; break; case 0x40: s3->accel.cy--; break; case 0x60: s3->accel.cx--; s3->accel.cy--; break; case 0x80: s3->accel.cx--; break; case 0xa0: s3->accel.cx--; s3->accel.cy++; break; case 0xc0: s3->accel.cy++; break; case 0xe0: s3->accel.cx++; s3->accel.cy++; break; default: break; } s3->accel.sy--; s3->accel.cx &= 0xfff; s3->accel.cy &= 0xfff; } s3->accel.cur_x = s3->accel.cx & 0xfff; s3->accel.cur_y = s3->accel.cy & 0xfff; } else { /*Bresenham*/ if (s3->accel.b2e8_pix && s3_cpu_src(s3) && (count == 16)) { /*Stupid undocumented 0xB2E8 on 911/924*/ count = s3->accel.maj_axis_pcnt + 1; s3->accel.temp_cnt = 16; } while (count-- && s3->accel.sy >= 0) { if (s3->accel.b2e8_pix && s3_cpu_src(s3) && !s3->accel.temp_cnt) { mix_dat >>= 16; s3->accel.temp_cnt = 16; } if ((s3->accel.cx & 0xfff) >= clip_l && (s3->accel.cx & 0xfff) <= clip_r && (s3->accel.cy & 0xfff) >= clip_t && (s3->accel.cy & 0xfff) <= clip_b) { switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = bkgd_color; break; case 1: src_dat = frgd_color; break; case 2: src_dat = cpu_dat; break; case 3: src_dat = 0; break; default: break; } if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || compare_mode < 2) { READ((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); MIX WRITE((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); } } if (s3->accel.b2e8_pix && s3_cpu_src(s3)) { if (s3->accel.temp_cnt > 0) { s3->accel.temp_cnt--; mix_dat <<= 1; mix_dat |= 1; } } else { mix_dat <<= 1; mix_dat |= 1; } if (s3->bpp == 0 && !s3->color_16bit) cpu_dat >>= 8; else cpu_dat >>= 16; if (!s3->accel.sy) break; if (s3->accel.cmd & 0x40) { if (s3->accel.cmd & 0x80) s3->accel.cy++; else s3->accel.cy--; if (s3->accel.err_term >= 0) { s3->accel.err_term += s3->accel.destx_distp; if (s3->accel.cmd & 0x20) s3->accel.cx++; else s3->accel.cx--; } else s3->accel.err_term += s3->accel.desty_axstp; } else { if (s3->accel.cmd & 0x20) s3->accel.cx++; else s3->accel.cx--; if (s3->accel.err_term >= 0) { s3->accel.err_term += s3->accel.destx_distp; if (s3->accel.cmd & 0x80) s3->accel.cy++; else s3->accel.cy--; } else s3->accel.err_term += s3->accel.desty_axstp; } s3->accel.sy--; s3->accel.cx &= 0xfff; s3->accel.cy &= 0xfff; } s3->accel.cur_x = s3->accel.cx & 0xfff; s3->accel.cur_y = s3->accel.cy & 0xfff; } break; case 2: /*Rectangle fill*/ if (!cpu_input) /*!cpu_input is trigger to start operation*/ { s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; s3->accel.sy = s3->accel.multifunc[0] & 0xfff; s3->accel.cx = s3->accel.cur_x & 0xfff; s3->accel.cy = s3->accel.cur_y & 0xfff; s3->accel.dest = dstbase + s3->accel.cy * s3->width; if (s3_cpu_src(s3)) { s3->data_available = 0; return; /*Wait for data from CPU*/ } else if (s3_cpu_dest(s3)) { s3->data_available = 1; return; } } frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; if (s3->accel.b2e8_pix && s3_cpu_src(s3) && count == 16) { /*Stupid undocumented 0xB2E8 on 911/924*/ count = s3->accel.maj_axis_pcnt + 1; s3->accel.temp_cnt = 16; } while (count-- && s3->accel.sy >= 0) { if (s3->accel.b2e8_pix && s3_cpu_src(s3) && s3->accel.temp_cnt == 0) { mix_dat >>= 16; s3->accel.temp_cnt = 16; } if ((s3->accel.cx & 0xfff) >= clip_l && (s3->accel.cx & 0xfff) <= clip_r && (s3->accel.cy & 0xfff) >= clip_t && (s3->accel.cy & 0xfff) <= clip_b) { if (s3_cpu_dest(s3) && ((s3->accel.multifunc[0xa] & 0xc0) == 0x00)) { mix_dat = mix_mask; /* Mix data = forced to foreground register. */ } else if (s3_cpu_dest(s3) && vram_mask) { /* Mix data = current video memory value. */ READ(s3->accel.dest + s3->accel.cx, mix_dat); mix_dat = ((mix_dat & rd_mask) == rd_mask); mix_dat = mix_dat ? mix_mask : 0; } if (s3_cpu_dest(s3)) { READ(s3->accel.dest + s3->accel.cx, src_dat); if (vram_mask) src_dat = ((src_dat & rd_mask) == rd_mask); } else { switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = bkgd_color; break; case 1: src_dat = frgd_color; break; case 2: src_dat = cpu_dat; break; case 3: src_dat = 0; break; default: break; } } if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || compare_mode < 2) { READ(s3->accel.dest + s3->accel.cx, dest_dat); MIX if (s3->accel.cmd & 0x10) { WRITE(s3->accel.dest + s3->accel.cx, dest_dat); } } } if (s3->accel.b2e8_pix && s3_cpu_src(s3)) { if (s3->accel.temp_cnt > 0) { s3->accel.temp_cnt--; mix_dat <<= 1; mix_dat |= 1; } } else { mix_dat <<= 1; mix_dat |= 1; } if (s3->bpp == 0 && !s3->color_16bit) cpu_dat >>= 8; else cpu_dat >>= 16; if (s3->accel.cmd & 0x20) s3->accel.cx++; else s3->accel.cx--; s3->accel.cx &= 0xfff; s3->accel.sx--; if (s3->accel.sx < 0) { if (s3->accel.cmd & 0x20) s3->accel.cx -= (s3->accel.maj_axis_pcnt & 0xfff) + 1; else s3->accel.cx += (s3->accel.maj_axis_pcnt & 0xfff) + 1; s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; if (s3->accel.cmd & 0x80) s3->accel.cy++; else s3->accel.cy--; s3->accel.cy &= 0xfff; s3->accel.dest = dstbase + s3->accel.cy * s3->width; s3->accel.sy--; if (cpu_input) { if (s3->accel.b2e8_pix) { s3->accel.cur_x = s3->accel.cx; s3->accel.cur_y = s3->accel.cy; } return; } if (s3->accel.sy < 0) { s3->accel.cur_x = s3->accel.cx; s3->accel.cur_y = s3->accel.cy; return; } } } break; case 3: /*Polygon Fill Solid (Vision868/968 and Trio64 only)*/ { int end_y1; int end_y2; if (s3->chip != S3_TRIO64 && s3->chip != S3_VISION968 && s3->chip != S3_VISION868) break; polygon_setup(s3); if ((s3->accel.cmd & 0x100) && !cpu_input) return; /*Wait for data from CPU*/ end_y1 = s3->accel.desty_axstp; end_y2 = s3->accel.desty_axstp2; frgd_mix = (s3->accel.frgd_mix >> 5) & 3; while ((s3->accel.poly_cy < end_y1) && (s3->accel.poly_cy2 < end_y2)) { int y = s3->accel.poly_cy; int x_count = ABS((s3->accel.poly_cx2 >> 20) - s3->accel.poly_x) + 1; s3->accel.dest = dstbase + y * s3->width; while (x_count-- && count--) { if ((s3->accel.poly_x & 0xfff) >= clip_l && (s3->accel.poly_x & 0xfff) <= clip_r && (s3->accel.poly_cy & 0xfff) >= clip_t && (s3->accel.poly_cy & 0xfff) <= clip_b) { switch (frgd_mix) { case 0: src_dat = s3->accel.bkgd_color; break; case 1: src_dat = s3->accel.frgd_color; break; case 2: src_dat = cpu_dat; break; case 3: src_dat = 0; /*Not supported?*/ break; default: break; } if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || compare_mode < 2) { READ(s3->accel.dest + s3->accel.poly_x, dest_dat); MIX if (s3->accel.cmd & 0x10) { WRITE(s3->accel.dest + s3->accel.poly_x, dest_dat); } } } if (s3->bpp == 0) cpu_dat >>= 8; else cpu_dat >>= 16; if (s3->accel.poly_x < (s3->accel.poly_cx2 >> 20)) s3->accel.poly_x++; else s3->accel.poly_x--; } s3->accel.poly_cx += s3->accel.poly_dx1; s3->accel.poly_cx2 += s3->accel.poly_dx2; s3->accel.poly_x = s3->accel.poly_cx >> 20; s3->accel.poly_cy++; s3->accel.poly_cy2++; if (!count) break; } s3->accel.cur_x = s3->accel.poly_cx & 0xfff; s3->accel.cur_y = s3->accel.poly_cy & 0xfff; s3->accel.cur_x2 = s3->accel.poly_cx2 & 0xfff; s3->accel.cur_y2 = s3->accel.poly_cy & 0xfff; } break; case 6: /*BitBlt*/ if (!cpu_input) { /*!cpu_input is trigger to start operation*/ s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; s3->accel.sy = s3->accel.multifunc[0] & 0xfff; s3->accel.dx = s3->accel.destx_distp & 0xfff; s3->accel.dy = s3->accel.desty_axstp & 0xfff; s3->accel.cx = s3->accel.cur_x & 0xfff; s3->accel.cy = s3->accel.cur_y & 0xfff; s3->accel.src = srcbase + s3->accel.cy * s3->width; s3->accel.dest = dstbase + s3->accel.dy * s3->width; } if ((s3->accel.cmd & 0x100) && !cpu_input) { return; /*Wait for data from CPU*/ } frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; if (!cpu_input && frgd_mix == 3 && !vram_mask && !compare_mode && (s3->accel.cmd & 0xa0) == 0xa0 && (s3->accel.frgd_mix & 0xf) == 7 && (s3->accel.bkgd_mix & 0xf) == 7) { while (1) { if ((s3->accel.dx & 0xfff) >= clip_l && (s3->accel.dx & 0xfff) <= clip_r && (s3->accel.dy & 0xfff) >= clip_t && (s3->accel.dy & 0xfff) <= clip_b) { READ(s3->accel.src + s3->accel.cx, src_dat); READ(s3->accel.dest + s3->accel.dx, dest_dat); dest_dat = (src_dat & wrt_mask) | (dest_dat & ~wrt_mask); WRITE(s3->accel.dest + s3->accel.dx, dest_dat); } s3->accel.cx++; s3->accel.dx++; s3->accel.sx--; s3->accel.dx &= 0xfff; if (s3->accel.sx < 0) { s3->accel.cx -= (s3->accel.maj_axis_pcnt & 0xfff) + 1; s3->accel.dx -= (s3->accel.maj_axis_pcnt & 0xfff) + 1; s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; s3->accel.cy++; s3->accel.dy++; s3->accel.dy &= 0xfff; s3->accel.src = srcbase + s3->accel.cy * s3->width; s3->accel.dest = dstbase + s3->accel.dy * s3->width; s3->accel.sy--; if (s3->accel.sy < 0) { s3->accel.destx_distp = s3->accel.dx; s3->accel.desty_axstp = s3->accel.dy; return; } } } } else { while (count-- && s3->accel.sy >= 0) { if ((s3->accel.dx & 0xfff) >= clip_l && (s3->accel.dx & 0xfff) <= clip_r && ((s3->accel.dy & 0xfff) >= clip_t && (s3->accel.dy & 0xfff) <= clip_b)) { if (vram_mask && (s3->accel.cmd & 0x10)) { READ(s3->accel.src + s3->accel.cx, mix_dat); mix_dat = ((mix_dat & rd_mask) == rd_mask); mix_dat = mix_dat ? mix_mask : 0; } switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = bkgd_color; break; case 1: src_dat = frgd_color; break; case 2: src_dat = cpu_dat; break; case 3: READ(s3->accel.src + s3->accel.cx, src_dat); if (vram_mask && (s3->accel.cmd & 0x10)) src_dat = ((src_dat & rd_mask) == rd_mask); break; default: break; } if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || compare_mode < 2) { READ(s3->accel.dest + s3->accel.dx, dest_dat); MIX if ((!(s3->accel.cmd & 0x10) && vram_mask) || (s3->accel.cmd & 0x10)) { WRITE(s3->accel.dest + s3->accel.dx, dest_dat); } } } mix_dat <<= 1; mix_dat |= 1; if (s3->bpp == 0 && !s3->color_16bit) cpu_dat >>= 8; else cpu_dat >>= 16; if (s3->accel.cmd & 0x20) { s3->accel.cx++; s3->accel.dx++; } else { s3->accel.cx--; s3->accel.dx--; } s3->accel.dx &= 0xfff; s3->accel.sx--; if (s3->accel.sx < 0) { if (s3->accel.cmd & 0x20) { s3->accel.cx -= ((s3->accel.maj_axis_pcnt & 0xfff) + 1); s3->accel.dx -= ((s3->accel.maj_axis_pcnt & 0xfff) + 1); } else { s3->accel.cx += ((s3->accel.maj_axis_pcnt & 0xfff) + 1); s3->accel.dx += ((s3->accel.maj_axis_pcnt & 0xfff) + 1); } s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; if (s3->accel.cmd & 0x80) { s3->accel.cy++; s3->accel.dy++; } else { s3->accel.cy--; s3->accel.dy--; } s3->accel.dy &= 0xfff; s3->accel.src = srcbase + s3->accel.cy * s3->width; s3->accel.dest = dstbase + s3->accel.dy * s3->width; s3->accel.sy--; if (cpu_input) return; if (s3->accel.sy < 0) { s3->accel.destx_distp = s3->accel.dx; s3->accel.desty_axstp = s3->accel.dy; return; } } } } break; case 7: /*Pattern fill - BitBlt but with source limited to 8x8*/ if (!cpu_input) /*!cpu_input is trigger to start operation*/ { s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; s3->accel.sy = s3->accel.multifunc[0] & 0xfff; s3->accel.dx = s3->accel.destx_distp & 0xfff; s3->accel.dy = s3->accel.desty_axstp & 0xfff; s3->accel.cx = s3->accel.cur_x & 0xfff; s3->accel.cy = s3->accel.cur_y & 0xfff; /*Align source with destination*/ s3->accel.pattern = (s3->accel.cy * s3->width) + s3->accel.cx; s3->accel.dest = dstbase + s3->accel.dy * s3->width; s3->accel.cx = s3->accel.dx & 7; s3->accel.cy = s3->accel.dy & 7; s3->accel.src = srcbase + s3->accel.pattern + (s3->accel.cy * s3->width); } if ((s3->accel.cmd & 0x100) && !cpu_input) { return; /*Wait for data from CPU*/ } frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; while (count-- && s3->accel.sy >= 0) { if ((s3->accel.dx & 0xfff) >= clip_l && (s3->accel.dx & 0xfff) <= clip_r && (s3->accel.dy & 0xfff) >= clip_t && (s3->accel.dy & 0xfff) <= clip_b) { if (vram_mask) { READ(s3->accel.src + s3->accel.cx, mix_dat); mix_dat = ((mix_dat & rd_mask) == rd_mask); mix_dat = mix_dat ? mix_mask : 0; } switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = s3->accel.bkgd_color; break; case 1: src_dat = s3->accel.frgd_color; break; case 2: src_dat = cpu_dat; break; case 3: READ(s3->accel.src + s3->accel.cx, src_dat); if (vram_mask) src_dat = ((src_dat & rd_mask) == rd_mask); break; default: break; } if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || compare_mode < 2) { READ(s3->accel.dest + s3->accel.dx, dest_dat); MIX if (s3->accel.cmd & 0x10) { WRITE(s3->accel.dest + s3->accel.dx, dest_dat); } } } mix_dat <<= 1; mix_dat |= 1; if (s3->bpp == 0 && !s3->color_16bit) cpu_dat >>= 8; else cpu_dat >>= 16; if (s3->accel.cmd & 0x20) { s3->accel.cx = ((s3->accel.cx + 1) & 7) | (s3->accel.cx & ~7); s3->accel.dx++; } else { s3->accel.cx = ((s3->accel.cx - 1) & 7) | (s3->accel.cx & ~7); s3->accel.dx--; } s3->accel.dx &= 0xfff; s3->accel.sx--; if (s3->accel.sx < 0) { if (s3->accel.cmd & 0x20) { s3->accel.cx = ((s3->accel.cx - (((s3->accel.maj_axis_pcnt & 0xfff) + 1))) & 7) | (s3->accel.cx & ~7); s3->accel.dx -= ((s3->accel.maj_axis_pcnt & 0xfff) + 1); } else { s3->accel.cx = ((s3->accel.cx + (((s3->accel.maj_axis_pcnt & 0xfff) + 1))) & 7) | (s3->accel.cx & ~7); s3->accel.dx += ((s3->accel.maj_axis_pcnt & 0xfff) + 1); } s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; if (s3->accel.cmd & 0x80) { s3->accel.cy = ((s3->accel.cy + 1) & 7) | (s3->accel.cy & ~7); s3->accel.dy++; } else { s3->accel.cy = ((s3->accel.cy - 1) & 7) | (s3->accel.cy & ~7); s3->accel.dy--; } s3->accel.dy &= 0xfff; s3->accel.src = srcbase + s3->accel.pattern + (s3->accel.cy * s3->width); s3->accel.dest = dstbase + s3->accel.dy * s3->width; s3->accel.sy--; if (cpu_input) { return; } if (s3->accel.sy < 0) { s3->accel.destx_distp = s3->accel.dx; s3->accel.desty_axstp = s3->accel.dy; return; } } } break; case 9: /*Polyline/2-Point Line (Vision868/968 and Trio64 only)*/ { int error; if (s3->chip != S3_TRIO64 && s3->chip != S3_VISION968 && s3->chip != S3_VISION868) break; if (!cpu_input) { s3->accel.dx = ABS(s3->accel.destx_distp - s3->accel.cur_x); if (s3->accel.destx_distp & 0x1000) s3->accel.dx |= ~0xfff; s3->accel.dy = ABS(s3->accel.desty_axstp - s3->accel.cur_y); if (s3->accel.desty_axstp & 0x1000) s3->accel.dy |= ~0xfff; s3->accel.cx = s3->accel.cur_x & 0xfff; s3->accel.cy = s3->accel.cur_y & 0xfff; } if ((s3->accel.cmd & 0x100) && !cpu_input) return; /*Wait for data from CPU*/ if (s3->accel.dx > s3->accel.dy) { error = s3->accel.dx / 2; while (s3->accel.cx != s3->accel.destx_distp && count--) { if ((s3->accel.cx & 0xfff) >= clip_l && (s3->accel.cx & 0xfff) <= clip_r && (s3->accel.cy & 0xfff) >= clip_t && (s3->accel.cy & 0xfff) <= clip_b) { src_dat = s3->accel.frgd_color; if (((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || compare_mode < 2) && (s3->accel.cmd & 0x10)) { READ((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); MIX if (s3->accel.cmd & 0x10) { WRITE((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); } } } error -= s3->accel.dy; if (error < 0) { error += s3->accel.dx; if (s3->accel.desty_axstp > s3->accel.cur_y) s3->accel.cy++; else s3->accel.cy--; s3->accel.cy &= 0xfff; } if (s3->accel.destx_distp > s3->accel.cur_x) s3->accel.cx++; else s3->accel.cx--; s3->accel.cx &= 0xfff; } } else { error = s3->accel.dy / 2; while (s3->accel.cy != s3->accel.desty_axstp && count--) { if ((s3->accel.cx & 0xfff) >= clip_l && (s3->accel.cx & 0xfff) <= clip_r && (s3->accel.cy & 0xfff) >= clip_t && (s3->accel.cy & 0xfff) <= clip_b) { src_dat = s3->accel.frgd_color; if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || compare_mode < 2) { READ((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); MIX if (s3->accel.cmd & 0x10) { WRITE((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); } } } error -= s3->accel.dx; if (error < 0) { error += s3->accel.dy; if (s3->accel.destx_distp > s3->accel.cur_x) s3->accel.cx++; else s3->accel.cx--; s3->accel.cx &= 0xfff; } if (s3->accel.desty_axstp > s3->accel.cur_y) s3->accel.cy++; else s3->accel.cy--; s3->accel.cy &= 0xfff; } } s3->accel.cur_x = s3->accel.cx; s3->accel.cur_y = s3->accel.cy; } break; case 11: /*Polygon Fill Pattern (Vision868/968 and Trio64 only)*/ { int end_y1; int end_y2; if (s3->chip != S3_TRIO64 && s3->chip != S3_VISION968 && s3->chip != S3_VISION868) break; polygon_setup(s3); if ((s3->accel.cmd & 0x100) && !cpu_input) return; /*Wait for data from CPU*/ end_y1 = s3->accel.desty_axstp; end_y2 = s3->accel.desty_axstp2; frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; while ((s3->accel.poly_cy < end_y1) && (s3->accel.poly_cy2 < end_y2)) { int y = s3->accel.poly_cy; int x_count = ABS((s3->accel.poly_cx2 >> 20) - s3->accel.poly_x) + 1; s3->accel.src = srcbase + s3->accel.pattern + ((y & 7) * s3->width); s3->accel.dest = dstbase + y * s3->width; while (x_count-- && count--) { int pat_x = s3->accel.poly_x & 7; if ((s3->accel.poly_x & 0xfff) >= clip_l && (s3->accel.poly_x & 0xfff) <= clip_r && (s3->accel.poly_cy & 0xfff) >= clip_t && (s3->accel.poly_cy & 0xfff) <= clip_b) { if (vram_mask) { READ(s3->accel.src + pat_x, mix_dat); mix_dat = ((mix_dat & rd_mask) == rd_mask); mix_dat = mix_dat ? mix_mask : 0; } switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = s3->accel.bkgd_color; break; case 1: src_dat = s3->accel.frgd_color; break; case 2: src_dat = cpu_dat; break; case 3: READ(s3->accel.src + pat_x, src_dat); if (vram_mask) src_dat = ((src_dat & rd_mask) == rd_mask); break; default: break; } if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || compare_mode < 2) { READ(s3->accel.dest + s3->accel.poly_x, dest_dat); MIX if (s3->accel.cmd & 0x10) { WRITE(s3->accel.dest + s3->accel.poly_x, dest_dat); } } } if (s3->bpp == 0) cpu_dat >>= 8; else cpu_dat >>= 16; mix_dat <<= 1; mix_dat |= 1; if (s3->accel.poly_x < (s3->accel.poly_cx2 >> 20)) s3->accel.poly_x++; else s3->accel.poly_x--; } s3->accel.poly_cx += s3->accel.poly_dx1; s3->accel.poly_cx2 += s3->accel.poly_dx2; s3->accel.poly_x = s3->accel.poly_cx >> 20; s3->accel.poly_cy++; s3->accel.poly_cy2++; if (!count) break; } s3->accel.cur_x = s3->accel.poly_cx & 0xfff; s3->accel.cur_y = s3->accel.poly_cy & 0xfff; s3->accel.cur_x2 = s3->accel.poly_cx2 & 0xfff; s3->accel.cur_y2 = s3->accel.poly_cy & 0xfff; } break; case 14: /*ROPBlt (Vision868/968 only)*/ if (s3->chip != S3_VISION968 && s3->chip != S3_VISION868) break; if (!cpu_input) /*!cpu_input is trigger to start operation*/ { s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; s3->accel.sy = s3->accel.multifunc[0] & 0xfff; s3->accel.dx = s3->accel.destx_distp & 0xfff; if (s3->accel.destx_distp & 0x1000) s3->accel.dx |= ~0xfff; s3->accel.dy = s3->accel.desty_axstp & 0xfff; if (s3->accel.desty_axstp & 0x1000) s3->accel.dy |= ~0xfff; s3->accel.cx = s3->accel.cur_x & 0xfff; s3->accel.cy = s3->accel.cur_y & 0xfff; s3->accel.px = s3->accel.pat_x & 0xfff; s3->accel.py = s3->accel.pat_y & 0xfff; s3->accel.dest = dstbase + (s3->accel.dy * s3->width); s3->accel.src = srcbase + (s3->accel.cy * s3->width); s3->accel.pattern = (s3->accel.py * s3->width); } if ((s3->accel.cmd & 0x100) && !cpu_input) return; /*Wait for data from CPU*/ frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; while (count-- && s3->accel.sy >= 0) { if ((s3->accel.dx & 0xfff) >= clip_l && (s3->accel.dx & 0xfff) <= clip_r && (s3->accel.dy & 0xfff) >= clip_t && (s3->accel.dy & 0xfff) <= clip_b) { switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: src_dat = s3->accel.bkgd_color; break; case 1: src_dat = s3->accel.frgd_color; break; case 2: src_dat = cpu_dat; break; case 3: READ(s3->accel.src + s3->accel.cx, src_dat); break; default: break; } if (s3->accel.ropmix & 0x100) { switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: pat_dat = s3->accel.pat_bg_color; break; case 1: pat_dat = s3->accel.pat_fg_color; break; case 2: pat_dat = cpu_dat; break; case 3: READ(s3->accel.pattern + s3->accel.px, pat_dat); break; default: break; } } else { switch ((mix_dat & mix_mask) ? frgd_mix : bkgd_mix) { case 0: pat_dat = s3->accel.bkgd_color; break; case 1: pat_dat = s3->accel.frgd_color; break; case 2: pat_dat = cpu_dat; break; case 3: READ(s3->accel.pattern + s3->accel.px, pat_dat); break; default: break; } } if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || compare_mode < 2) { READ(s3->accel.dest + s3->accel.dx, dest_dat); ROPMIX if (s3->accel.cmd & 0x10) { WRITE(s3->accel.dest + s3->accel.dx, out); } } } mix_dat <<= 1; mix_dat |= 1; if (s3->bpp == 0) cpu_dat >>= 8; else cpu_dat >>= 16; if (s3->accel.cmd & 0x20) { s3->accel.cx++; s3->accel.dx++; s3->accel.px++; } else { s3->accel.cx--; s3->accel.dx--; s3->accel.px--; } s3->accel.sx--; if (s3->accel.sx < 0) { if (s3->accel.cmd & 0x20) { s3->accel.cx -= ((s3->accel.maj_axis_pcnt & 0xfff) + 1); s3->accel.dx -= ((s3->accel.maj_axis_pcnt & 0xfff) + 1); s3->accel.px -= ((s3->accel.maj_axis_pcnt & 0xfff) + 1); } else { s3->accel.cx += ((s3->accel.maj_axis_pcnt & 0xfff) + 1); s3->accel.dx += ((s3->accel.maj_axis_pcnt & 0xfff) + 1); s3->accel.px += ((s3->accel.maj_axis_pcnt & 0xfff) + 1); } s3->accel.sx = s3->accel.maj_axis_pcnt & 0xfff; if (s3->accel.cmd & 0x80) { s3->accel.cy++; s3->accel.dy++; s3->accel.py++; } else { s3->accel.cy--; s3->accel.dy--; s3->accel.py--; } s3->accel.src = srcbase + (s3->accel.cy * s3->width); s3->accel.dest = dstbase + (s3->accel.dy * s3->width); s3->accel.pattern = (s3->accel.py * s3->width); s3->accel.sy--; if (cpu_input /* && (s3->accel.multifunc[0xa] & 0xc0) == 0x80*/) return; if (s3->accel.sy < 0) { return; } } } break; default: break; } } static uint8_t s3_pci_read(UNUSED(int func), int addr, void *priv) { const s3_t *s3 = (s3_t *) priv; const svga_t *svga = &s3->svga; switch (addr) { case 0x00: return 0x33; /*'S3'*/ case 0x01: return 0x53; case 0x02: return s3->id_ext_pci; case 0x03: return (s3->chip == S3_TRIO64V2) ? 0x89 : 0x88; case PCI_REG_COMMAND: if (s3->chip == S3_VISION968 || s3->chip == S3_VISION868) return s3->pci_regs[PCI_REG_COMMAND] | 0x80; /*Respond to IO and memory accesses*/ else return s3->pci_regs[PCI_REG_COMMAND]; /*Respond to IO and memory accesses*/ break; case 0x07: return (s3->chip == S3_TRIO64V2) ? (s3->pci_regs[0x07] & 0x36) : (1 << 1); /*Medium DEVSEL timing*/ case 0x08: switch (s3->chip) { /*Revision ID*/ case S3_TRIO64V: return 0x40; case S3_TRIO64V2: return 0x16; /*Confirmed on an onboard 64V2/DX*/ default: return 0x00; } break; case 0x09: return 0; /*Programming interface*/ case 0x0a: if (s3->chip >= S3_TRIO32 || s3->chip == S3_VISION968 || s3->chip == S3_VISION868) return 0x00; /*Supports VGA interface*/ else return 0x01; case 0x0b: if (s3->chip >= S3_TRIO32 || s3->chip == S3_VISION968 || s3->chip == S3_VISION868) return 0x03; else return 0x00; case 0x0d: return (s3->chip == S3_TRIO64V2) ? (s3->pci_regs[0x0d] & 0xf8) : 0x00; case 0x10: return 0x00; /*Linear frame buffer address*/ case 0x11: return 0x00; case 0x12: if (svga->crtc[0x53] & 0x08) return 0x00; else return (svga->crtc[0x5a] & 0x80); break; case 0x13: if (svga->crtc[0x53] & 0x08) { return (s3->chip >= S3_TRIO64V) ? (svga->crtc[0x59] & 0xfc) : (svga->crtc[0x59] & 0xfe); } else { return svga->crtc[0x59]; } break; case 0x30: return s3->has_bios ? (s3->pci_regs[0x30] & 0x01) : 0x00; /*BIOS ROM address*/ case 0x31: return 0x00; case 0x32: return s3->has_bios ? s3->pci_regs[0x32] : 0x00; case 0x33: return s3->has_bios ? s3->pci_regs[0x33] : 0x00; case 0x3c: return s3->int_line; case 0x3d: return PCI_INTA; case 0x3e: return (s3->chip == S3_TRIO64V2) ? 0x04 : 0x00; case 0x3f: return (s3->chip == S3_TRIO64V2) ? 0xff : 0x00; default: break; } return 0; } static void s3_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) { s3_t *s3 = (s3_t *) priv; svga_t *svga = &s3->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: if (s3->chip == S3_TRIO64V2) return; break; case PCI_REG_COMMAND: if (val & PCI_COMMAND_IO) s3_io_set(s3); else s3_io_remove(s3); s3->pci_regs[PCI_REG_COMMAND] = (val & 0x23); s3_updatemapping(s3); break; case 0x07: if (s3->chip == S3_TRIO64V2) { s3->pci_regs[0x07] = val & 0x3e; return; } break; case 0x0d: if (s3->chip == S3_TRIO64V2) { s3->pci_regs[0x0d] = val & 0xf8; return; } break; case 0x12: if (!(svga->crtc[0x53] & 0x08)) { svga->crtc[0x5a] = (svga->crtc[0x5a] & 0x7f) | (val & 0x80); s3_updatemapping(s3); } break; case 0x13: if (svga->crtc[0x53] & 0x08) { svga->crtc[0x59] = (s3->chip >= S3_TRIO64V) ? (val & 0xfc) : (val & 0xfe); } else { svga->crtc[0x59] = val; } s3_updatemapping(s3); break; case 0x30: case 0x32: case 0x33: if (!s3->has_bios) return; s3->pci_regs[addr] = val; if (s3->pci_regs[0x30] & 0x01) { uint32_t biosaddr = (s3->pci_regs[0x32] << 16) | (s3->pci_regs[0x33] << 24); mem_mapping_set_addr(&s3->bios_rom.mapping, biosaddr, 0x8000); } else { mem_mapping_disable(&s3->bios_rom.mapping); } return; case 0x3c: s3->int_line = val; return; default: break; } } static void fifo_thread(void *param) { s3_t *s3 = (s3_t *) param; uint64_t start_time; uint64_t end_time; while (s3->fifo_thread_run) { thread_set_event(s3->fifo_not_full_event); thread_wait_event(s3->wake_fifo_thread, -1); thread_reset_event(s3->wake_fifo_thread); s3->blitter_busy = 1; while (!FIFO_EMPTY) { start_time = plat_timer_read(); fifo_entry_t *fifo = &s3->fifo[s3->fifo_read_idx & FIFO_MASK]; switch (fifo->addr_type & FIFO_TYPE) { case FIFO_WRITE_BYTE: s3_accel_write_fifo(s3, fifo->addr_type & FIFO_ADDR, fifo->val); break; case FIFO_WRITE_WORD: s3_accel_write_fifo_w(s3, fifo->addr_type & FIFO_ADDR, fifo->val); break; case FIFO_WRITE_DWORD: s3_accel_write_fifo_l(s3, fifo->addr_type & FIFO_ADDR, fifo->val); break; case FIFO_OUT_BYTE: s3_accel_out_fifo(s3, fifo->addr_type & FIFO_ADDR, fifo->val); break; case FIFO_OUT_WORD: s3_accel_out_fifo_w(s3, fifo->addr_type & FIFO_ADDR, fifo->val); break; case FIFO_OUT_DWORD: s3_accel_out_fifo_l(s3, fifo->addr_type & FIFO_ADDR, fifo->val); break; default: break; } s3->fifo_read_idx++; fifo->addr_type = FIFO_INVALID; if (FIFO_ENTRIES > 0xe000) thread_set_event(s3->fifo_not_full_event); end_time = plat_timer_read(); s3->blitter_time += (end_time - start_time); } s3->blitter_busy = 0; s3->subsys_stat |= INT_FIFO_EMP; s3_update_irqs(s3); } } static int vram_sizes[] = { 7, /*512 kB*/ 6, /*1 MB*/ 4, /*2 MB*/ 0, 0, /*4 MB*/ 0, 0, /*6 MB*/ 0, 3 /*8 MB*/ }; static void s3_reset(void *priv) { s3_t *s3 = (s3_t *) priv; svga_t *svga = &s3->svga; memset(svga->crtc, 0x00, sizeof(svga->crtc)); svga->crtc[0] = 63; svga->crtc[6] = 255; svga->dispontime = 1000ULL << 32; svga->dispofftime = 1000ULL << 32; svga->bpp = 8; if (s3->pci) svga->crtc[0x36] = 2 | (3 << 2) | (1 << 4); else if (s3->vlb) svga->crtc[0x36] = 1 | (3 << 2) | (1 << 4); else svga->crtc[0x36] = 3 | (1 << 4); if (s3->chip >= S3_86C928) svga->crtc[0x36] |= (vram_sizes[s3->vram] << 5); else svga->crtc[0x36] |= ((s3->vram == 1) ? 0x00 : 0x20) | 0x80; svga->crtc[0x37] = 1 | (7 << 5); if (s3->chip >= S3_86C928) svga->crtc[0x37] |= 0x04; s3_io_set(s3); memset(s3->pci_regs, 0x00, 256); s3->pci_regs[PCI_REG_COMMAND] = 7; s3->pci_regs[0x30] = 0x00; s3->pci_regs[0x32] = 0x0c; s3->pci_regs[0x33] = 0x00; if (s3->chip <= S3_86C924) s3->accel_start = s3_911_accel_start; else s3->accel_start = s3_accel_start; switch (s3->card_type) { case S3_MIROCRYSTAL8S_805: case S3_MIROCRYSTAL10SD_805: svga->crtc[0x5a] = 0x0a; svga->getclock = sdac_getclock; break; case S3_SPEA_MIRAGE_86C801: case S3_SPEA_MIRAGE_86C805: svga->crtc[0x5a] = 0x0a; break; case S3_PHOENIX_86C801: case S3_PHOENIX_86C805: svga->crtc[0x5a] = 0x0a; break; case S3_METHEUS_86C928: case S3_SPEA_MERCURY_LITE_PCI: svga->crtc[0x5a] = 0x0a; break; case S3_PARADISE_BAHAMAS64: case S3_PHOENIX_VISION864: case S3_MIROCRYSTAL20SD_864: svga->crtc[0x5a] = 0x0a; break; case S3_DIAMOND_STEALTH64_964: case S3_ELSAWIN2KPROX_964: case S3_MIROCRYSTAL20SV_964: svga->crtc[0x5a] = 0x0a; break; case S3_ELSAWIN2KPROX: case S3_SPEA_MERCURY_P64V: case S3_MIROVIDEO40SV_ERGO_968: case S3_NUMBER9_9FX_771: case S3_PHOENIX_VISION968: if (s3->pci) { svga->crtc[0x53] = 0x18; svga->crtc[0x58] = 0x10; svga->crtc[0x59] = 0x70; svga->crtc[0x5a] = 0x00; svga->crtc[0x6c] = 1; } else { svga->crtc[0x53] = 0x00; svga->crtc[0x59] = 0x00; svga->crtc[0x5a] = 0x0a; } break; case S3_NUMBER9_9FX_531: case S3_PHOENIX_VISION868: if (s3->pci) { svga->crtc[0x53] = 0x18; svga->crtc[0x58] = 0x10; svga->crtc[0x59] = 0x70; svga->crtc[0x5a] = 0x00; svga->crtc[0x6c] = 1; } else { svga->crtc[0x53] = 0x00; svga->crtc[0x59] = 0x00; svga->crtc[0x5a] = 0x0a; } break; case S3_PHOENIX_TRIO64: case S3_PHOENIX_TRIO64_ONBOARD: case S3_STB_POWERGRAPH_64_VIDEO: case S3_CARDEX_TRIO64VPLUS: case S3_PHOENIX_TRIO64VPLUS: case S3_PHOENIX_TRIO64VPLUS_ONBOARD: case S3_DIAMOND_STEALTH64_764: case S3_SPEA_MIRAGE_P64: case S3_NUMBER9_9FX: if (s3->chip == S3_TRIO64V) svga->crtc[0x53] = 0x08; break; case S3_TRIO64V2_DX: svga->crtc[0x53] = 0x08; svga->crtc[0x59] = 0x70; svga->crtc[0x5a] = 0x00; svga->crtc[0x6c] = 1; s3->pci_regs[0x05] = 0; s3->pci_regs[0x06] = 0; s3->pci_regs[0x07] = 2; s3->pci_regs[0x3d] = 1; s3->pci_regs[0x3e] = 4; s3->pci_regs[0x3f] = 0xff; break; default: break; } if (s3->has_bios) { if (s3->pci) mem_mapping_disable(&s3->bios_rom.mapping); } s3_updatemapping(s3); mem_mapping_disable(&s3->mmio_mapping); mem_mapping_disable(&s3->new_mmio_mapping); } static void * s3_init(const device_t *info) { const char *bios_fn; int chip; int stepping; s3_t *s3 = malloc(sizeof(s3_t)); svga_t *svga = &s3->svga; int vram; uint32_t vram_size; switch (info->local) { case S3_ORCHID_86C911: bios_fn = ROM_ORCHID_86C911; chip = S3_86C911; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c911); break; case S3_DIAMOND_STEALTH_VRAM: bios_fn = ROM_DIAMOND_STEALTH_VRAM; chip = S3_86C911; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c911); break; case S3_AMI_86C924: bios_fn = ROM_AMI_86C924; chip = S3_86C924; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c911); break; case S3_SPEA_MIRAGE_86C801: bios_fn = ROM_SPEA_MIRAGE_86C801; chip = S3_86C801; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c801); break; case S3_86C805_ONBOARD: bios_fn = NULL; chip = S3_86C805; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c805); break; case S3_SPEA_MIRAGE_86C805: bios_fn = ROM_SPEA_MIRAGE_86C805; chip = S3_86C805; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c805); break; case S3_MIROCRYSTAL8S_805: bios_fn = ROM_MIROCRYSTAL8S_805; chip = S3_86C805; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c805); break; case S3_MIROCRYSTAL10SD_805: bios_fn = ROM_MIROCRYSTAL10SD_805; chip = S3_86C805; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c805); break; case S3_PHOENIX_86C801: bios_fn = ROM_PHOENIX_86C80X; chip = S3_86C801; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c801); break; case S3_PHOENIX_86C805: bios_fn = ROM_PHOENIX_86C80X; chip = S3_86C805; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c805); break; case S3_METHEUS_86C928: bios_fn = ROM_METHEUS_86C928; chip = S3_86C928; if (info->flags & DEVICE_VLB) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c805); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c801); break; case S3_SPEA_MERCURY_LITE_PCI: bios_fn = ROM_SPEA_MERCURY_LITE_PCI; chip = S3_86C928PCI; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_86c928pci); break; case S3_MIROCRYSTAL20SD_864: bios_fn = ROM_MIROCRYSTAL20SD_864_VLB; chip = S3_VISION864; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision864_vlb); break; case S3_PARADISE_BAHAMAS64: bios_fn = ROM_PARADISE_BAHAMAS64; chip = S3_VISION864; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision864_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision864_vlb); break; case S3_PHOENIX_VISION864: bios_fn = ROM_PHOENIX_VISION864; chip = S3_VISION864; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision864_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision864_vlb); break; case S3_NUMBER9_9FX_531: bios_fn = ROM_NUMBER9_9FX_531; chip = S3_VISION868; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision868_pci); break; case S3_PHOENIX_VISION868: bios_fn = ROM_PHOENIX_VISION868; chip = S3_VISION868; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision868_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision868_vlb); break; case S3_DIAMOND_STEALTH64_964: bios_fn = ROM_DIAMOND_STEALTH64_964; chip = S3_VISION964; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision964_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision964_vlb); break; case S3_MIROCRYSTAL20SV_964: chip = S3_VISION964; if (info->flags & DEVICE_PCI) { bios_fn = ROM_MIROCRYSTAL20SV_964_PCI; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision964_pci); } else { bios_fn = ROM_MIROCRYSTAL20SV_964_VLB; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision964_vlb); } break; case S3_MIROVIDEO40SV_ERGO_968: bios_fn = ROM_MIROVIDEO40SV_ERGO_968_PCI; chip = S3_VISION968; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision968_pci); break; case S3_NUMBER9_9FX_771: bios_fn = ROM_NUMBER9_9FX_771; chip = S3_VISION968; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision968_pci); break; case S3_PHOENIX_VISION968: bios_fn = ROM_PHOENIX_VISION968; chip = S3_VISION968; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision968_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision968_vlb); break; case S3_ELSAWIN2KPROX_964: bios_fn = ROM_ELSAWIN2KPROX_964; chip = S3_VISION964; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision964_pci); break; case S3_ELSAWIN2KPROX: bios_fn = ROM_ELSAWIN2KPROX; chip = S3_VISION968; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision968_pci); break; case S3_SPEA_MERCURY_P64V: bios_fn = ROM_SPEA_MERCURY_P64V; chip = S3_VISION968; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_vision968_pci); break; case S3_PHOENIX_TRIO32: bios_fn = ROM_PHOENIX_TRIO32; chip = S3_TRIO32; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio32_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio32_vlb); break; case S3_PHOENIX_TRIO32_ONBOARD: bios_fn = NULL; chip = S3_TRIO32; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio32_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio32_vlb); break; case S3_DIAMOND_STEALTH_SE: bios_fn = ROM_DIAMOND_STEALTH_SE; chip = S3_TRIO32; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio32_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio32_vlb); break; case S3_PHOENIX_TRIO64: bios_fn = ROM_PHOENIX_TRIO64; chip = S3_TRIO64; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_vlb); break; case S3_SPEA_MIRAGE_P64: bios_fn = ROM_SPEA_MIRAGE_P64; chip = S3_TRIO64; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_vlb); break; case S3_PHOENIX_TRIO64_ONBOARD: bios_fn = NULL; chip = S3_TRIO64; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_vlb); break; case S3_STB_POWERGRAPH_64_VIDEO: bios_fn = ROM_STB_POWERGRAPH_64_VIDEO; chip = S3_TRIO64V; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_vlb); break; case S3_PHOENIX_TRIO64VPLUS: bios_fn = ROM_PHOENIX_TRIO64VPLUS; chip = S3_TRIO64V; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_vlb); break; case S3_PHOENIX_TRIO64VPLUS_ONBOARD: bios_fn = NULL; chip = S3_TRIO64V; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_vlb); break; case S3_CARDEX_TRIO64VPLUS: bios_fn = ROM_CARDEX_TRIO64VPLUS; chip = S3_TRIO64V; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64vp_cardex_pci); break; case S3_DIAMOND_STEALTH64_764: bios_fn = ROM_DIAMOND_STEALTH64_764; chip = S3_TRIO64; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_stealth64_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_stealth64_vlb); break; case S3_NUMBER9_9FX: bios_fn = ROM_NUMBER9_9FX; chip = S3_TRIO64; if (info->flags & DEVICE_PCI) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_pci); else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_vlb); break; case S3_TRIO64V2_DX: bios_fn = ROM_TRIO64V2_DX_VBE20; chip = S3_TRIO64V2; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_pci); break; case S3_TRIO64V2_DX_ONBOARD: bios_fn = NULL; chip = S3_TRIO64V2; video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_s3_trio64_pci); break; default: free(s3); return NULL; } memset(s3, 0, sizeof(s3_t)); vram = device_get_config_int("memory"); if (vram) vram_size = vram << 20; else vram_size = 512 << 10; s3->vram_mask = vram_size - 1; s3->vram = vram; s3->has_bios = (bios_fn != NULL); if (s3->has_bios) { rom_init(&s3->bios_rom, bios_fn, 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); if (info->flags & DEVICE_PCI) mem_mapping_disable(&s3->bios_rom.mapping); } s3->pci = !!(info->flags & DEVICE_PCI); s3->vlb = !!(info->flags & DEVICE_VLB); mem_mapping_add(&s3->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, &s3->svga); /*It's hardcoded to 0xa0000 before the Trio64V+ and expects so*/ if (chip >= S3_TRIO64V) mem_mapping_add(&s3->mmio_mapping, 0, 0, s3_accel_read, s3_accel_read_w, s3_accel_read_l, s3_accel_write, s3_accel_write_w, s3_accel_write_l, NULL, MEM_MAPPING_EXTERNAL, s3); else mem_mapping_add(&s3->mmio_mapping, 0xa0000, 0x10000, s3_accel_read, s3_accel_read_w, s3_accel_read_l, s3_accel_write, s3_accel_write_w, s3_accel_write_l, NULL, MEM_MAPPING_EXTERNAL, s3); mem_mapping_add(&s3->new_mmio_mapping, 0, 0, s3_accel_read, s3_accel_read_w, s3_accel_read_l, s3_accel_write, s3_accel_write_w, s3_accel_write_l, NULL, MEM_MAPPING_EXTERNAL, s3); mem_mapping_disable(&s3->mmio_mapping); mem_mapping_disable(&s3->new_mmio_mapping); if (chip == S3_VISION964 || chip == S3_VISION968) svga_init(info, &s3->svga, s3, vram_size, s3_recalctimings, s3_in, s3_out, NULL, NULL); else { if (chip >= S3_TRIO64V) { svga_init(info, svga, s3, vram_size, s3_trio64v_recalctimings, s3_in, s3_out, s3_hwcursor_draw, s3_trio64v_overlay_draw); } else { svga_init(info, svga, s3, vram_size, s3_recalctimings, s3_in, s3_out, s3_hwcursor_draw, NULL); } } svga->hwcursor.cur_ysize = 64; switch (chip) { case S3_VISION964: switch (info->local) { case S3_ELSAWIN2KPROX_964: svga->dac_hwcursor_draw = ibm_rgb528_hwcursor_draw; break; default: svga->dac_hwcursor_draw = bt48x_hwcursor_draw; break; } break; case S3_VISION968: switch (info->local) { case S3_ELSAWIN2KPROX: case S3_PHOENIX_VISION968: case S3_NUMBER9_9FX_771: svga->dac_hwcursor_draw = ibm_rgb528_hwcursor_draw; break; case S3_SPEA_MERCURY_P64V: case S3_MIROVIDEO40SV_ERGO_968: svga->dac_hwcursor_draw = tvp3026_hwcursor_draw; break; default: break; } break; } if (chip >= S3_VISION964) { switch (vram) { case 0: /* 512 kB */ svga->vram_mask = (1 << 19) - 1; svga->vram_max = 1 << 19; break; case 1: /* 1 MB */ /* VRAM in first MB, mirrored in 2nd MB, 3rd and 4th MBs are open bus. This works with the #9 9FX BIOS, and matches how my real Trio64 behaves, but does not work with the Phoenix EDO BIOS. Possibly an FPM/EDO difference? */ svga->vram_mask = (1 << 20) - 1; svga->vram_max = 1 << 20; break; case 2: default: /*2 MB */ /* VRAM in first 2 MB, 3rd and 4th MBs are open bus. */ svga->vram_mask = (2 << 20) - 1; svga->vram_max = 2 << 20; break; case 4: /*4MB*/ svga->vram_mask = (4 << 20) - 1; svga->vram_max = 4 << 20; break; case 8: /*8MB*/ svga->vram_mask = (8 << 20) - 1; svga->vram_max = 8 << 20; break; } } if (s3->pci) svga->crtc[0x36] = 2 | (3 << 2) | (1 << 4); else if (s3->vlb) svga->crtc[0x36] = 1 | (3 << 2) | (1 << 4); else svga->crtc[0x36] = 3 | (1 << 4); if (chip >= S3_86C928) svga->crtc[0x36] |= (vram_sizes[vram] << 5); else { svga->crtc[0x36] |= ((vram == 1) ? 0x00 : 0x20) | 0x98; svga->crtc[0x41] = (vram == 1) ? 0x10 : 0x00; } svga->crtc[0x37] = 1 | (7 << 5); if (chip >= S3_86C928) svga->crtc[0x37] |= 0x04; svga->vblank_start = s3_vblank_start; s3_io_set(s3); s3->pci_regs[PCI_REG_COMMAND] = 7; s3->pci_regs[0x30] = 0x00; s3->pci_regs[0x32] = 0x0c; s3->pci_regs[0x33] = 0x00; s3->chip = chip; s3->int_line = 0; s3->card_type = info->local; svga->force_old_addr = 1; if (s3->chip <= S3_86C924) s3->accel_start = s3_911_accel_start; else s3->accel_start = s3_accel_start; switch (s3->card_type) { case S3_ORCHID_86C911: case S3_DIAMOND_STEALTH_VRAM: svga->decode_mask = (1 << 20) - 1; stepping = 0x81; /*86C911*/ s3->id = stepping; s3->id_ext = stepping; s3->id_ext_pci = 0; s3->packed_mmio = 0; s3->width = 1024; svga->ramdac = device_add(&sc11483_ramdac_device); svga->clock_gen = device_add(&av9194_device); svga->getclock = av9194_getclock; break; case S3_AMI_86C924: svga->decode_mask = (1 << 20) - 1; stepping = 0x82; /*86C911A/86C924*/ s3->id = stepping; s3->id_ext = stepping; s3->id_ext_pci = 0; s3->packed_mmio = 0; s3->width = 1024; svga->ramdac = device_add(&att490_ramdac_device); svga->clock_gen = device_add(&ics2494an_305_device); svga->getclock = ics2494_getclock; break; case S3_MIROCRYSTAL8S_805: case S3_MIROCRYSTAL10SD_805: svga->decode_mask = (2 << 20) - 1; stepping = 0xa0; /*86C801/86C805*/ s3->id = stepping; s3->id_ext = stepping; s3->id_ext_pci = 0; s3->packed_mmio = 0; svga->crtc[0x5a] = 0x0a; svga->ramdac = device_add(&gendac_ramdac_device); svga->clock_gen = svga->ramdac; svga->getclock = sdac_getclock; break; case S3_SPEA_MIRAGE_86C801: case S3_SPEA_MIRAGE_86C805: svga->decode_mask = (2 << 20) - 1; stepping = 0xa2; /*86C801/86C805*/ s3->id = stepping; s3->id_ext = stepping; s3->id_ext_pci = 0; s3->packed_mmio = 0; svga->crtc[0x5a] = 0x0a; svga->ramdac = device_add(&att491_ramdac_device); svga->clock_gen = device_add(&av9194_device); svga->getclock = av9194_getclock; break; case S3_86C805_ONBOARD: svga->decode_mask = (2 << 20) - 1; stepping = 0xa0; /*86C801/86C805*/ s3->id = stepping; s3->id_ext = stepping; s3->id_ext_pci = 0; s3->packed_mmio = 0; svga->crtc[0x5a] = 0x0a; svga->ramdac = device_add(&att490_ramdac_device); svga->clock_gen = device_add(&av9194_device); svga->getclock = av9194_getclock; break; case S3_PHOENIX_86C801: case S3_PHOENIX_86C805: svga->decode_mask = (2 << 20) - 1; stepping = 0xa0; /*86C801/86C805*/ s3->id = stepping; s3->id_ext = stepping; s3->id_ext_pci = 0; s3->packed_mmio = 0; svga->crtc[0x5a] = 0x0a; svga->ramdac = device_add(&att492_ramdac_device); svga->clock_gen = device_add(&av9194_device); svga->getclock = av9194_getclock; break; case S3_METHEUS_86C928: svga->decode_mask = (4 << 20) - 1; stepping = 0x91; /*86C928D*/ s3->id = stepping; s3->id_ext = stepping; s3->id_ext_pci = 0; s3->packed_mmio = 0; svga->crtc[0x5a] = 0x0a; svga->ramdac = device_add(&bt485_ramdac_device); svga->clock_gen = device_add(&icd2061_device); svga->getclock = icd2061_getclock; break; case S3_SPEA_MERCURY_LITE_PCI: svga->decode_mask = (4 << 20) - 1; stepping = 0xb0; /*86C928PCI*/ s3->id = stepping; s3->id_ext = stepping; s3->id_ext_pci = stepping; s3->packed_mmio = 0; svga->crtc[0x5a] = 0x0a; svga->ramdac = device_add(&sc1502x_ramdac_device); svga->clock_gen = device_add(&av9194_device); svga->getclock = av9194_getclock; break; case S3_PARADISE_BAHAMAS64: case S3_PHOENIX_VISION864: case S3_MIROCRYSTAL20SD_864: /*BIOS 3.xx has a SDAC ramdac.*/ svga->decode_mask = (8 << 20) - 1; if (info->local == S3_PARADISE_BAHAMAS64) stepping = 0xc0; /*Vision864*/ else stepping = 0xc1; /*Vision864P*/ s3->id = stepping; s3->id_ext = s3->id_ext_pci = stepping; s3->packed_mmio = 0; svga->crtc[0x5a] = 0x0a; svga->ramdac = device_add(&sdac_ramdac_device); svga->clock_gen = svga->ramdac; svga->getclock = sdac_getclock; break; case S3_DIAMOND_STEALTH64_964: case S3_ELSAWIN2KPROX_964: case S3_MIROCRYSTAL20SV_964: svga->decode_mask = (8 << 20) - 1; stepping = 0xd0; /*Vision964*/ s3->id = stepping; s3->id_ext = s3->id_ext_pci = stepping; s3->packed_mmio = 1; svga->crtc[0x5a] = 0x0a; switch (info->local) { case S3_ELSAWIN2KPROX_964: svga->ramdac = device_add(&ibm_rgb528_ramdac_device); svga->clock_gen = device_add(&icd2061_device); svga->getclock = icd2061_getclock; break; default: svga->ramdac = device_add(&bt485_ramdac_device); svga->clock_gen = device_add(&icd2061_device); svga->getclock = icd2061_getclock; break; } break; case S3_ELSAWIN2KPROX: case S3_SPEA_MERCURY_P64V: case S3_MIROVIDEO40SV_ERGO_968: case S3_NUMBER9_9FX_771: case S3_PHOENIX_VISION968: svga->decode_mask = (8 << 20) - 1; s3->id = 0xe1; /*Vision968*/ s3->id_ext = s3->id_ext_pci = 0xf0; s3->packed_mmio = 1; if (s3->pci) { svga->crtc[0x53] = 0x18; svga->crtc[0x58] = 0x10; svga->crtc[0x59] = 0x70; svga->crtc[0x5a] = 0x00; svga->crtc[0x6c] = 1; } else { svga->crtc[0x53] = 0x00; svga->crtc[0x59] = 0x00; svga->crtc[0x5a] = 0x0a; } switch (info->local) { case S3_ELSAWIN2KPROX: case S3_PHOENIX_VISION968: case S3_NUMBER9_9FX_771: svga->ramdac = device_add(&ibm_rgb528_ramdac_device); svga->clock_gen = device_add(&icd2061_device); svga->getclock = icd2061_getclock; break; default: svga->ramdac = device_add(&tvp3026_ramdac_device); svga->clock_gen = svga->ramdac; svga->getclock = tvp3026_getclock; svga->conv_16to32 = tvp3026_conv_16to32; break; } break; case S3_NUMBER9_9FX_531: case S3_PHOENIX_VISION868: svga->decode_mask = (8 << 20) - 1; s3->id = 0xe1; /*Vision868*/ s3->id_ext = 0x90; s3->id_ext_pci = 0x80; s3->packed_mmio = 1; if (s3->pci) { svga->crtc[0x53] = 0x18; svga->crtc[0x58] = 0x10; svga->crtc[0x59] = 0x70; svga->crtc[0x5a] = 0x00; svga->crtc[0x6c] = 1; } else { svga->crtc[0x53] = 0x00; svga->crtc[0x59] = 0x00; svga->crtc[0x5a] = 0x0a; } if (info->local == S3_NUMBER9_9FX_531) { svga->ramdac = device_add(&att498_ramdac_device); svga->clock_gen = device_add(&icd2061_device); svga->getclock = icd2061_getclock; } else { svga->ramdac = device_add(&sdac_ramdac_device); svga->clock_gen = svga->ramdac; svga->getclock = sdac_getclock; } break; case S3_PHOENIX_TRIO32: case S3_PHOENIX_TRIO32_ONBOARD: case S3_DIAMOND_STEALTH_SE: svga->decode_mask = (4 << 20) - 1; s3->id = 0xe1; /*Trio32*/ s3->id_ext = 0x10; s3->id_ext_pci = 0x11; s3->packed_mmio = 1; svga->clock_gen = s3; svga->getclock = s3_trio64_getclock; break; case S3_PHOENIX_TRIO64: case S3_PHOENIX_TRIO64_ONBOARD: case S3_STB_POWERGRAPH_64_VIDEO: case S3_PHOENIX_TRIO64VPLUS: case S3_PHOENIX_TRIO64VPLUS_ONBOARD: case S3_CARDEX_TRIO64VPLUS: case S3_DIAMOND_STEALTH64_764: case S3_SPEA_MIRAGE_P64: if (device_get_config_int("memory") == 1) svga->vram_max = 1 << 20; /* Phoenix BIOS does not expect VRAM to be mirrored. */ /* Fall over. */ fallthrough; case S3_NUMBER9_9FX: svga->decode_mask = (4 << 20) - 1; s3->id = 0xe1; /*Trio64*/ s3->id_ext = s3->id_ext_pci = 0x11; s3->packed_mmio = 1; if (s3->chip == S3_TRIO64V) svga->crtc[0x53] = 0x08; svga->clock_gen = s3; svga->getclock = s3_trio64_getclock; break; case S3_TRIO64V2_DX: case S3_TRIO64V2_DX_ONBOARD: svga->decode_mask = (4 << 20) - 1; s3->id = 0xe1; /*Trio64V2*/ s3->id_ext = s3->id_ext_pci = 0x01; s3->packed_mmio = 1; svga->crtc[0x53] = 0x08; svga->crtc[0x59] = 0x70; svga->crtc[0x5a] = 0x00; svga->crtc[0x6c] = 1; s3->pci_regs[0x05] = 0; s3->pci_regs[0x06] = 0; s3->pci_regs[0x07] = 2; s3->pci_regs[0x3d] = 1; s3->pci_regs[0x3e] = 4; s3->pci_regs[0x3f] = 0xff; svga->clock_gen = s3; svga->getclock = s3_trio64_getclock; break; default: return NULL; } if (s3->pci) { if (bios_fn == NULL) pci_add_card(PCI_ADD_VIDEO, s3_pci_read, s3_pci_write, s3, &s3->pci_slot); else pci_add_card(PCI_ADD_NORMAL, s3_pci_read, s3_pci_write, s3, &s3->pci_slot); } s3->i2c = i2c_gpio_init("ddc_s3"); s3->ddc = ddc_init(i2c_gpio_get_bus(s3->i2c)); s3->wake_fifo_thread = thread_create_event(); s3->fifo_not_full_event = thread_create_event(); s3->fifo_thread_run = 1; s3->fifo_thread = thread_create(fifo_thread, s3); return s3; } static int s3_orchid_86c911_available(void) { return rom_present(ROM_ORCHID_86C911); } static int s3_diamond_stealth_vram_available(void) { return rom_present(ROM_DIAMOND_STEALTH_VRAM); } static int s3_ami_86c924_available(void) { return rom_present(ROM_AMI_86C924); } static int s3_spea_mirage_86c801_available(void) { return rom_present(ROM_SPEA_MIRAGE_86C801); } static int s3_spea_mirage_86c805_available(void) { return rom_present(ROM_SPEA_MIRAGE_86C805); } static int s3_phoenix_86c80x_available(void) { return rom_present(ROM_PHOENIX_86C80X); } static int s3_mirocrystal_8s_805_available(void) { return rom_present(ROM_MIROCRYSTAL8S_805); } static int s3_mirocrystal_10sd_805_available(void) { return rom_present(ROM_MIROCRYSTAL10SD_805); } static int s3_metheus_86c928_available(void) { return rom_present(ROM_METHEUS_86C928); } static int s3_spea_mercury_lite_pci_available(void) { return rom_present(ROM_SPEA_MERCURY_LITE_PCI); } static int s3_bahamas64_available(void) { return rom_present(ROM_PARADISE_BAHAMAS64); } static int s3_phoenix_vision864_available(void) { return rom_present(ROM_PHOENIX_VISION864); } static int s3_9fx_531_available(void) { return rom_present(ROM_NUMBER9_9FX_531); } static int s3_phoenix_vision868_available(void) { return rom_present(ROM_PHOENIX_VISION868); } static int s3_mirocrystal_20sv_964_vlb_available(void) { return rom_present(ROM_MIROCRYSTAL20SV_964_VLB); } static int s3_mirocrystal_20sv_964_pci_available(void) { return rom_present(ROM_MIROCRYSTAL20SV_964_PCI); } static int s3_diamond_stealth64_964_available(void) { return rom_present(ROM_DIAMOND_STEALTH64_964); } static int s3_mirovideo_40sv_ergo_968_pci_available(void) { return rom_present(ROM_MIROVIDEO40SV_ERGO_968_PCI); } static int s3_9fx_771_available(void) { return rom_present(ROM_NUMBER9_9FX_771); } static int s3_phoenix_vision968_available(void) { return rom_present(ROM_PHOENIX_VISION968); } static int s3_mirocrystal_20sd_864_vlb_available(void) { return rom_present(ROM_MIROCRYSTAL20SD_864_VLB); } static int s3_spea_mercury_p64v_pci_available(void) { return rom_present(ROM_SPEA_MERCURY_P64V); } static int s3_elsa_winner2000_pro_x_964_available(void) { return rom_present(ROM_ELSAWIN2KPROX_964); } static int s3_elsa_winner2000_pro_x_available(void) { return rom_present(ROM_ELSAWIN2KPROX); } static int s3_phoenix_trio32_available(void) { return rom_present(ROM_PHOENIX_TRIO32); } static int s3_diamond_stealth_se_available(void) { return rom_present(ROM_DIAMOND_STEALTH_SE); } static int s3_9fx_available(void) { return rom_present(ROM_NUMBER9_9FX); } static int s3_spea_mirage_p64_vlb_available(void) { return rom_present(ROM_SPEA_MIRAGE_P64); } static int s3_phoenix_trio64_available(void) { return rom_present(ROM_PHOENIX_TRIO64); } static int s3_stb_powergraph_64_video_available(void) { return rom_present(ROM_STB_POWERGRAPH_64_VIDEO); } static int s3_phoenix_trio64vplus_available(void) { return rom_present(ROM_PHOENIX_TRIO64VPLUS); } static int s3_cardex_trio64vplus_available(void) { return rom_present(ROM_PHOENIX_TRIO64VPLUS); } static int s3_diamond_stealth64_764_available(void) { return rom_present(ROM_DIAMOND_STEALTH64_764); } static int s3_trio64v2_dx_available(void) { return rom_present(ROM_TRIO64V2_DX_VBE20); } static void s3_close(void *priv) { s3_t *s3 = (s3_t *) priv; s3->fifo_thread_run = 0; thread_set_event(s3->wake_fifo_thread); thread_wait(s3->fifo_thread); thread_destroy_event(s3->fifo_not_full_event); thread_destroy_event(s3->wake_fifo_thread); svga_close(&s3->svga); ddc_close(s3->ddc); i2c_gpio_close(s3->i2c); free(s3); } static void s3_speed_changed(void *priv) { s3_t *s3 = (s3_t *) priv; svga_recalctimings(&s3->svga); } static void s3_force_redraw(void *priv) { s3_t *s3 = (s3_t *) priv; s3->svga.fullchange = s3->svga.monitor->mon_changeframecount; } static const device_config_t s3_orchid_86c911_config[] = { { .name = "memory", .description = "Memory size", .type = CONFIG_SELECTION, .default_int = 1, .selection = { { .description = "512 KB", .value = 0 }, { .description = "1 MB", .value = 1 }, { .description = "" } } }, { .type = CONFIG_END } }; static const device_config_t s3_9fx_config[] = { { .name = "memory", .description = "Memory size", .type = CONFIG_SELECTION, .default_int = 2, .selection = { { .description = "1 MB", .value = 1 }, { .description = "2 MB", .value = 2 }, /*Trio64 also supports 4 MB, however the Number Nine BIOS does not*/ { .description = "" } } }, { .type = CONFIG_END } }; static const device_config_t s3_phoenix_trio32_config[] = { { .name = "memory", .description = "Memory size", .type = CONFIG_SELECTION, .default_int = 2, .selection = { { .description = "512 KB", .value = 0 }, { .description = "1 MB", .value = 1 }, { .description = "2 MB", .value = 2 }, { .description = "" } } }, { .type = CONFIG_END } }; static const device_config_t s3_standard_config[] = { { .name = "memory", .description = "Memory size", .type = CONFIG_SELECTION, .default_int = 4, .selection = { { .description = "1 MB", .value = 1 }, { .description = "2 MB", .value = 2 }, { .description = "4 MB", .value = 4 }, { .description = "" } } }, { .type = CONFIG_END } }; static const device_config_t s3_968_config[] = { { .name = "memory", .description = "Memory size", .type = CONFIG_SELECTION, .default_int = 4, .selection = { { .description = "1 MB", .value = 1 }, { .description = "2 MB", .value = 2 }, { .description = "4 MB", .value = 4 }, { .description = "8 MB", .value = 8 }, { .description = "" } } }, { .type = CONFIG_END } }; const device_t s3_orchid_86c911_isa_device = { .name = "S3 86c911 ISA (Orchid Fahrenheit 1280)", .internal_name = "orchid_s3_911", .flags = DEVICE_AT | DEVICE_ISA, .local = S3_ORCHID_86C911, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_orchid_86c911_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_orchid_86c911_config }; const device_t s3_diamond_stealth_vram_isa_device = { .name = "S3 86c911 ISA (Diamond Stealth VRAM)", .internal_name = "stealthvram_isa", .flags = DEVICE_AT | DEVICE_ISA, .local = S3_DIAMOND_STEALTH_VRAM, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_diamond_stealth_vram_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_orchid_86c911_config }; const device_t s3_ami_86c924_isa_device = { .name = "S3 86c924 ISA (AMI)", .internal_name = "ami_s3_924", .flags = DEVICE_AT | DEVICE_ISA, .local = S3_AMI_86C924, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_ami_86c924_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_orchid_86c911_config }; const device_t s3_spea_mirage_86c801_isa_device = { .name = "S3 86c801 ISA (SPEA Mirage ISA)", .internal_name = "px_s3_v7_801_isa", .flags = DEVICE_AT | DEVICE_ISA, .local = S3_SPEA_MIRAGE_86C801, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_spea_mirage_86c801_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_86c805_onboard_vlb_device = { .name = "S3 86c805 VLB On-Board", .internal_name = "px_s3_805_onboard_vlb", .flags = DEVICE_VLB, .local = S3_86C805_ONBOARD, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = NULL }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_spea_mirage_86c805_vlb_device = { .name = "S3 86c805 VLB (SPEA Mirage VL)", .internal_name = "px_s3_v7_805_vlb", .flags = DEVICE_VLB, .local = S3_SPEA_MIRAGE_86C805, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_spea_mirage_86c805_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_mirocrystal_8s_805_vlb_device = { .name = "S3 86c805 VLB (MiroCRYSTAL 8S)", .internal_name = "mirocrystal8s_vlb", .flags = DEVICE_VLB, .local = S3_MIROCRYSTAL8S_805, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_mirocrystal_8s_805_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_mirocrystal_10sd_805_vlb_device = { .name = "S3 86c805 VLB (MiroCRYSTAL 10SD)", .internal_name = "mirocrystal10sd_vlb", .flags = DEVICE_VLB, .local = S3_MIROCRYSTAL10SD_805, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_mirocrystal_10sd_805_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_phoenix_86c801_isa_device = { .name = "S3 86c801 ISA (Phoenix)", .internal_name = "px_86c801_isa", .flags = DEVICE_AT | DEVICE_ISA, .local = S3_PHOENIX_86C801, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_86c80x_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_phoenix_86c805_vlb_device = { .name = "S3 86c805 VLB (Phoenix)", .internal_name = "px_86c805_vlb", .flags = DEVICE_VLB, .local = S3_PHOENIX_86C805, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_86c80x_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_metheus_86c928_isa_device = { .name = "S3 86c928 ISA (Metheus Premier 928)", .internal_name = "metheus928_isa", .flags = DEVICE_AT | DEVICE_ISA, .local = S3_METHEUS_86C928, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_metheus_86c928_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_metheus_86c928_vlb_device = { .name = "S3 86c928 VLB (Metheus Premier 928)", .internal_name = "metheus928_vlb", .flags = DEVICE_VLB, .local = S3_METHEUS_86C928, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_metheus_86c928_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_spea_mercury_lite_86c928_pci_device = { .name = "S3 86c928 PCI (SPEA Mercury Lite)", .internal_name = "spea_mercurylite_pci", .flags = DEVICE_PCI, .local = S3_SPEA_MERCURY_LITE_PCI, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_spea_mercury_lite_pci_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_orchid_86c911_config }; const device_t s3_mirocrystal_20sd_864_vlb_device = { .name = "S3 Vision864 VLB (MiroCRYSTAL 20SD)", .internal_name = "mirocrystal20sd_vlb", .flags = DEVICE_VLB, .local = S3_MIROCRYSTAL20SD_864, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_mirocrystal_20sd_864_vlb_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_bahamas64_vlb_device = { .name = "S3 Vision864 VLB (Paradise Bahamas 64)", .internal_name = "bahamas64_vlb", .flags = DEVICE_VLB, .local = S3_PARADISE_BAHAMAS64, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_bahamas64_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_bahamas64_pci_device = { .name = "S3 Vision864 PCI (Paradise Bahamas 64)", .internal_name = "bahamas64_pci", .flags = DEVICE_PCI, .local = S3_PARADISE_BAHAMAS64, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_bahamas64_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_mirocrystal_20sv_964_vlb_device = { .name = "S3 Vision964 VLB (MiroCRYSTAL 20SV)", .internal_name = "mirocrystal20sv_vlb", .flags = DEVICE_VLB, .local = S3_MIROCRYSTAL20SV_964, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_mirocrystal_20sv_964_vlb_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_mirocrystal_20sv_964_pci_device = { .name = "S3 Vision964 PCI (MiroCRYSTAL 20SV)", .internal_name = "mirocrystal20sv_pci", .flags = DEVICE_PCI, .local = S3_MIROCRYSTAL20SV_964, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_mirocrystal_20sv_964_pci_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_diamond_stealth64_964_vlb_device = { .name = "S3 Vision964 VLB (Diamond Stealth64 VRAM)", .internal_name = "stealth64v_vlb", .flags = DEVICE_VLB, .local = S3_DIAMOND_STEALTH64_964, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_diamond_stealth64_964_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_diamond_stealth64_964_pci_device = { .name = "S3 Vision964 PCI (Diamond Stealth64 VRAM)", .internal_name = "stealth64v_pci", .flags = DEVICE_PCI, .local = S3_DIAMOND_STEALTH64_964, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_diamond_stealth64_964_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_9fx_771_pci_device = { .name = "S3 Vision968 PCI (Number 9 9FX 771)", .internal_name = "n9_9fx_771_pci", .flags = DEVICE_PCI, .local = S3_NUMBER9_9FX_771, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_9fx_771_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_968_config }; const device_t s3_phoenix_vision968_pci_device = { .name = "S3 Vision968 PCI (Phoenix)", .internal_name = "px_vision968_pci", .flags = DEVICE_PCI, .local = S3_PHOENIX_VISION968, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_vision968_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_phoenix_vision968_vlb_device = { .name = "S3 Vision968 VLB (Phoenix)", .internal_name = "px_vision968_vlb", .flags = DEVICE_VLB, .local = S3_PHOENIX_VISION968, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_vision968_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_mirovideo_40sv_ergo_968_pci_device = { .name = "S3 Vision968 PCI (MiroVIDEO 40SV Ergo)", .internal_name = "mirovideo40sv_pci", .flags = DEVICE_PCI, .local = S3_MIROVIDEO40SV_ERGO_968, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_mirovideo_40sv_ergo_968_pci_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_spea_mercury_p64v_pci_device = { .name = "S3 Vision968 PCI (SPEA Mercury P64V)", .internal_name = "spea_mercury64p_pci", .flags = DEVICE_PCI, .local = S3_SPEA_MERCURY_P64V, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_spea_mercury_p64v_pci_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_9fx_vlb_device = { .name = "S3 Trio64 VLB (Number 9 9FX 330)", .internal_name = "n9_9fx_vlb", .flags = DEVICE_VLB, .local = S3_NUMBER9_9FX, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_9fx_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_9fx_pci_device = { .name = "S3 Trio64 PCI (Number 9 9FX 330)", .internal_name = "n9_9fx_pci", .flags = DEVICE_PCI, .local = S3_NUMBER9_9FX, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_9fx_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_phoenix_trio32_onboard_vlb_device = { .name = "S3 Trio32 VLB On-Board (Phoenix)", .internal_name = "px_trio32_onboard_vlb", .flags = DEVICE_VLB, .local = S3_PHOENIX_TRIO32_ONBOARD, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = NULL }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_phoenix_trio32_config }; const device_t s3_phoenix_trio32_vlb_device = { .name = "S3 Trio32 VLB (Phoenix)", .internal_name = "px_trio32_vlb", .flags = DEVICE_VLB, .local = S3_PHOENIX_TRIO32, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_trio32_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_phoenix_trio32_config }; const device_t s3_phoenix_trio32_onboard_pci_device = { .name = "S3 Trio32 PCI On-Board (Phoenix)", .internal_name = "px_trio32_onboard_pci", .flags = DEVICE_PCI, .local = S3_PHOENIX_TRIO32_ONBOARD, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = NULL }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_phoenix_trio32_config }; const device_t s3_phoenix_trio32_pci_device = { .name = "S3 Trio32 PCI (Phoenix)", .internal_name = "px_trio32_pci", .flags = DEVICE_PCI, .local = S3_PHOENIX_TRIO32, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_trio32_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_phoenix_trio32_config }; const device_t s3_diamond_stealth_se_vlb_device = { .name = "S3 Trio32 VLB (Diamond Stealth SE)", .internal_name = "stealthse_vlb", .flags = DEVICE_VLB, .local = S3_DIAMOND_STEALTH_SE, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_diamond_stealth_se_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_phoenix_trio32_config }; const device_t s3_diamond_stealth_se_pci_device = { .name = "S3 Trio32 PCI (Diamond Stealth SE)", .internal_name = "stealthse_pci", .flags = DEVICE_PCI, .local = S3_DIAMOND_STEALTH_SE, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_diamond_stealth_se_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_phoenix_trio32_config }; const device_t s3_phoenix_trio64_vlb_device = { .name = "S3 Trio64 VLB (Phoenix)", .internal_name = "px_trio64_vlb", .flags = DEVICE_VLB, .local = S3_PHOENIX_TRIO64, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_trio64_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_phoenix_trio64_onboard_pci_device = { .name = "S3 Trio64 PCI On-Board (Phoenix)", .internal_name = "px_trio64_onboard_pci", .flags = DEVICE_PCI, .local = S3_PHOENIX_TRIO64_ONBOARD, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = NULL }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_phoenix_trio64_pci_device = { .name = "S3 Trio64 PCI (Phoenix)", .internal_name = "px_trio64_pci", .flags = DEVICE_PCI, .local = S3_PHOENIX_TRIO64, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_trio64_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_stb_powergraph_64_video_vlb_device = { .name = "S3 Trio64V+ (STB PowerGraph 64 Video) VLB", .internal_name = "stb_trio64vplus_vlb", .flags = DEVICE_VLB, .local = S3_STB_POWERGRAPH_64_VIDEO, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_stb_powergraph_64_video_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_phoenix_trio32_config }; const device_t s3_phoenix_trio64vplus_onboard_pci_device = { .name = "S3 Trio64V+ PCI On-Board (Phoenix)", .internal_name = "px_trio64vplus_onboard_pci", .flags = DEVICE_PCI, .local = S3_PHOENIX_TRIO64VPLUS_ONBOARD, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = NULL }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_phoenix_trio64vplus_pci_device = { .name = "S3 Trio64V+ PCI (Phoenix)", .internal_name = "px_trio64vplus_pci", .flags = DEVICE_PCI, .local = S3_PHOENIX_TRIO64VPLUS, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_trio64vplus_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_cardex_trio64vplus_pci_device = { .name = "S3 Trio64V+ PCI (Cardex)", .internal_name = "cardex_trio64vplus_pci", .flags = DEVICE_PCI, .local = S3_CARDEX_TRIO64VPLUS, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_cardex_trio64vplus_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_phoenix_vision864_vlb_device = { .name = "S3 Vision864 VLB (Phoenix)", .internal_name = "px_vision864_vlb", .flags = DEVICE_VLB, .local = S3_PHOENIX_VISION864, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_vision864_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_phoenix_vision864_pci_device = { .name = "S3 Vision864 PCI (Phoenix)", .internal_name = "px_vision864_pci", .flags = DEVICE_PCI, .local = S3_PHOENIX_VISION864, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_vision864_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_9fx_531_pci_device = { .name = "S3 Vision868 PCI (Number 9 9FX 531)", .internal_name = "n9_9fx_531_pci", .flags = DEVICE_PCI, .local = S3_NUMBER9_9FX_531, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_9fx_531_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_phoenix_vision868_vlb_device = { .name = "S3 Vision868 VLB (Phoenix)", .internal_name = "px_vision868_vlb", .flags = DEVICE_VLB, .local = S3_PHOENIX_VISION868, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_vision868_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_phoenix_vision868_pci_device = { .name = "S3 Vision868 PCI (Phoenix)", .internal_name = "px_vision868_pci", .flags = DEVICE_PCI, .local = S3_PHOENIX_VISION868, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_phoenix_vision868_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_diamond_stealth64_vlb_device = { .name = "S3 Trio64 VLB (Diamond Stealth64 DRAM)", .internal_name = "stealth64d_vlb", .flags = DEVICE_VLB, .local = S3_DIAMOND_STEALTH64_764, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_diamond_stealth64_764_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_diamond_stealth64_pci_device = { .name = "S3 Trio64 PCI (Diamond Stealth64 DRAM)", .internal_name = "stealth64d_pci", .flags = DEVICE_PCI, .local = S3_DIAMOND_STEALTH64_764, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_diamond_stealth64_764_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_spea_mirage_p64_vlb_device = { .name = "S3 Trio64 VLB (SPEA Mirage P64)", .internal_name = "spea_miragep64_vlb", .flags = DEVICE_VLB, .local = S3_SPEA_MIRAGE_P64, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_spea_mirage_p64_vlb_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_9fx_config }; const device_t s3_elsa_winner2000_pro_x_964_pci_device = { .name = "S3 Vision964 PCI (ELSA Winner 2000 Pro/X)", .internal_name = "elsawin2kprox_964_pci", .flags = DEVICE_PCI, .local = S3_ELSAWIN2KPROX_964, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_elsa_winner2000_pro_x_964_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_968_config }; const device_t s3_elsa_winner2000_pro_x_pci_device = { .name = "S3 Vision968 PCI (ELSA Winner 2000 Pro/X)", .internal_name = "elsawin2kprox_pci", .flags = DEVICE_PCI, .local = S3_ELSAWIN2KPROX, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_elsa_winner2000_pro_x_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_968_config }; const device_t s3_trio64v2_dx_pci_device = { .name = "S3 Trio64V2/DX PCI", .internal_name = "trio64v2dx_pci", .flags = DEVICE_PCI, .local = S3_TRIO64V2_DX, .init = s3_init, .close = s3_close, .reset = s3_reset, { .available = s3_trio64v2_dx_available }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config }; const device_t s3_trio64v2_dx_onboard_pci_device = { .name = "S3 Trio64V2/DX On-Board PCI", .internal_name = "trio64v2dx_onboard_pci", .flags = DEVICE_PCI, .local = S3_TRIO64V2_DX_ONBOARD, .init = s3_init, .close = s3_close, .reset = NULL, { .available = NULL }, .speed_changed = s3_speed_changed, .force_redraw = s3_force_redraw, .config = s3_standard_config };