/* * This file is part of the AdvanceMAME project * * Copyright (C) 1999-2000 Andrea Mazzoleni * * The contents of this file are free for non-commercial use. * Any commercial use is forbidden. * * This video driver is STRONGLY derived from the video drivers of the * VSyncMAME project by S.Sakamaki (Saka) */ //.s #define MAME #ifdef MAME #include "mamalleg.h" #include "driver.h" #else #include static void logerror(char* txt, ...) { } #endif //.e #include #include #include #include #include #include #include #include "s3.h" #include "mode15k.h" static void SetS3ExtRegister(CRTCPARAM *cp); static void SetS3Interlace(int flag, CRTCPARAM* cp); static void SetS3VClock(int mul, int _div, int p); static long GetS3VclkParam(long dotclock, int *s_mul, int *s_div, int *s_p); static void SetS3DoubleScan(int flag); static void SetS3PLL(void); struct S3ID { unsigned value; const char* name; int supported; int mul_min; int mul_max; int div_min; int div_max; int p_min; int p_max; int ref; /* kHz */ int vco_min; /* kHz */ int vco_max; /* kHz */ int clock_type; }; /* Interrupt List 60 5631h 86C325 ViRGE 3D GUI Accelerator 8800h Vision 866 8801h Vision 964 8810h S3 Trio32 8811h S3 Trio64, or Trio64V+ 8812h S3 Trio64UV+ 8813h S3 Trio64? v3 883Dh S3 ViRGE/VX 888xh S3 868 88Bxh S3 928 88C0h S3 864-1 88C1h S3 864-2 88C2h S3 864-3 88C3h S3 864-4 88D0h S3 964-1 88D1h S3 964-2 88D2h S3 964-3 88D3h S3 964-4 88F0h S3 968 88F1h S3 968-2 88F2h S3 968-3 88F3h S3 968-3 8900h S3 Trio64 V2/DX GUI Accelerator 8901h S3 775 Trio64V2 8902h S3 PLATO/PX 8A01h S3 ViRGE/DX or ViRGE/GX 8A10h ??? 8C00h ??? 8C01h ??? */ /* From XFree 4.0.0 #define PCI_CHIP_PLATO 0x0551 #define PCI_CHIP_VIRGE 0x5631 #define PCI_CHIP_TRIO 0x8811 #define PCI_CHIP_AURORA64VP 0x8812 #define PCI_CHIP_TRIO64UVP 0x8814 #define PCI_CHIP_VIRGE_VX 0x883D #define PCI_CHIP_868 0x8880 #define PCI_CHIP_928 0x88B0 #define PCI_CHIP_864_0 0x88C0 #define PCI_CHIP_864_1 0x88C1 #define PCI_CHIP_964_0 0x88D0 #define PCI_CHIP_964_1 0x88D1 #define PCI_CHIP_968 0x88F0 #define PCI_CHIP_TRIO64V2_DXGX 0x8901 #define PCI_CHIP_PLATO_PX 0x8902 #define PCI_CHIP_Trio3D 0x8904 #define PCI_CHIP_Trio3D_2X 0x8A13 #define PCI_CHIP_VIRGE_DXGX 0x8A01 #define PCI_CHIP_VIRGE_GX2 0x8A10 #define PCI_CHIP_Savage3D 0x8A20 #define PCI_CHIP_Savage3D_MV 0x8A21 #define PCI_CHIP_Savage4 0x8A22 #define PCI_CHIP_Savage2000 0x9102 #define PCI_CHIP_VIRGE_MX 0x8C01 #define PCI_CHIP_VIRGE_MXPLUS 0x8C01 #define PCI_CHIP_VIRGE_MXP 0x8C03 */ /* Clock */ #define S3_CLOCK_TRIO 1 #define S3_CLOCK_VIRGEGX2 2 #define S3_CLOCK_AURORA 3 /* Configuration */ #define S3_CONF_UNSUPPORTED 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 #define S3_CONF_TRIO 1, 1+2, 127+2, 1+2, 31+2, 0, 3, 14318, 135000, 270000, S3_CLOCK_TRIO #define S3_CONF_TRIO64V2 1, 1+2, 127+2, 1+2, 31+2, 0, 4, 14318, 170000, 340000, S3_CLOCK_TRIO #define S3_CONF_VIRGEDXGX 1, 1+2, 127+2, 1+2, 31+2, 0, 4, 14318, 135000, 270000, S3_CLOCK_TRIO #define S3_CONF_VIRGEVX 1, 1+2, 127+2, 1+2, 31+2, 0, 4, 14318, 220000, 440000, S3_CLOCK_TRIO #define S3_CONF_VIRGEGX2 1, 1+2, 127+2, 1+2, 31+2, 0, 4, 14318, 170000, 340000, S3_CLOCK_VIRGEGX2 #define S3_CONF_TRIO3D 1, 1+2, 127+2, 1+2, 31+2, 0, 4, 14318, 230000, 460000, S3_CLOCK_TRIO #define S3_CONF_AURORA 1, 1+2, 127+2, 1+2, 63+2, 0, 3, 14318, 135000, 270000, S3_CLOCK_AURORA static struct S3ID S3IDList[] = { { 0x0551, "S3 PLATO/PX", S3_CONF_UNSUPPORTED }, { 0x5631, "S3 86C325 ViRGE 3D GUI Accelerator", S3_CONF_TRIO }, { 0x8800, "S3 Vision 866", S3_CONF_TRIO }, { 0x8801, "S3 Vision 964", S3_CONF_TRIO }, { 0x8810, "S3 Trio32", S3_CONF_TRIO }, { 0x8811, "S3 Trio64, or Trio64V+", S3_CONF_TRIO }, { 0x8812, "S3 Aurora64V+", S3_CONF_AURORA }, { 0x8814, "S3 Trio64UV+", S3_CONF_TRIO }, { 0x883D, "S3 ViRGE/VX", S3_CONF_VIRGEVX }, { 0x8880, "S3 868", S3_CONF_TRIO }, { 0x88B0, "S3 928", S3_CONF_TRIO }, { 0x88C0, "S3 864-1", S3_CONF_TRIO }, { 0x88C1, "S3 864-2", S3_CONF_TRIO }, { 0x88C2, "S3 864-3", S3_CONF_TRIO }, { 0x88C3, "S3 864-4", S3_CONF_TRIO }, { 0x88D0, "S3 964-1", S3_CONF_TRIO }, { 0x88D1, "S3 964-2", S3_CONF_TRIO }, { 0x88D2, "S3 964-3", S3_CONF_TRIO }, { 0x88D3, "S3 964-4", S3_CONF_TRIO }, { 0x88F0, "S3 968", S3_CONF_TRIO }, { 0x88F1, "S3 968-2", S3_CONF_TRIO }, { 0x88F2, "S3 968-3", S3_CONF_TRIO }, { 0x88F3, "S3 968-3", S3_CONF_TRIO }, { 0x8900, "S3 Trio64 V2/DX GUI Accelerator", S3_CONF_TRIO }, { 0x8901, "S3 Trio64V2", S3_CONF_TRIO64V2 }, /* TESTED */ { 0x8902, "S3 PLATO/PX", S3_CONF_UNSUPPORTED }, { 0x8904, "S3 Trio3D", S3_CONF_TRIO3D }, { 0x8A13, "S3 Trio3D/2X", S3_CONF_TRIO3D }, { 0x8A01, "S3 ViRGE/DX or ViRGE/GX", S3_CONF_VIRGEDXGX }, /* TESTED */ { 0x8A10, "S3 ViRGE/GX2", S3_CONF_VIRGEGX2 }, { 0x8A20, "S3 Savage3D", S3_CONF_UNSUPPORTED }, { 0x8A21, "S3 Savage3D MacroVision", S3_CONF_UNSUPPORTED }, { 0x8A22, "S3 Savage4", S3_CONF_UNSUPPORTED }, { 0x9102, "S3 Savage 2000", S3_CONF_UNSUPPORTED }, { 0x8C01, "S3 ViRGE/MX", S3_CONF_UNSUPPORTED }, { 0x8C02, "S3 ViRGE/MX+", S3_CONF_UNSUPPORTED }, { 0x8C03, "S3 ViRGE/MX+MV", S3_CONF_UNSUPPORTED }, { 0xca00, "S3 SonicVibes,", S3_CONF_UNSUPPORTED }, { 0,0, S3_CONF_UNSUPPORTED } }; static struct S3ID* s3_card; /* S3 detected */ int detectS3(void) { __dpmi_regs r; int i; r.d.eax = 0x0000b101; // PCI BIOS - INSTALLATION CHECK r.d.edi = 0x00000000; __dpmi_int(0x1a, &r); if( r.d.edx != 0x20494350 ){ // ' ICP' logerror("s3: PCI BIOS not Installed.\n"); return 0; } for(i=0;S3IDList[i].name;++i) { r.d.eax = 0x0000b102; // PCI BIOS - FIND PCI DEVICE r.d.ecx = S3IDList[i].value; // device ID r.d.edx = 0x00005333; // vendor ID r.d.esi = 0x00000000; // device index __dpmi_int(0x1a, &r); if( r.h.ah == 0 ){ break; } } if(!S3IDList[i].name){ logerror("s3: not found\n"); return 0; } logerror("s3: found %s, device id %04x\n", S3IDList[i].name, S3IDList[i].value); if (!S3IDList[i].supported) { logerror("s3: card not supported\n"); return 0; } s3_card = S3IDList + i; #ifdef MAME DisableStretch(); #endif return 1; } void resetS3(void) { } int setS3(void) { CRTCPARAM cp; int mul,_div,p; unsigned d0; if(GetMode15KHz(&cp) == 0) return 0; //.s cp.HBStart = cp.HSStart; cp.HBEnd = cp.HTotal - 16; cp.VBStart = cp.VDisp; cp.VBEnd = cp.VTotal - 2; //.e d0 = GetCrtcReg(0x67); if ( (d0 & 0xF0) == (5 << 4) || (d0 & 0xF0) == (3 << 4)) { logerror("s3: double h value for 16 bit mode\n"); cp.HDisp *= 2; cp.HTotal *= 2; cp.HBStart *= 2; cp.HBEnd *= 2; cp.HSStart *= 2; cp.HSEnd *= 2; } logerror("s3: clock requested:%.2f\n", (double)cp.dotclockHz / 1000000 ); cp.dotclockHz = GetS3VclkParam(cp.dotclockHz, &mul, &_div, &p); if (cp.dotclockHz < 0) { logerror("s3: clock out of range\n"); return 0; } logerror("s3: clock selected:%.2f MHz\n", (double)cp.dotclockHz / 1000000 ); cp.vert001Hz = CalcVert001Hz(cp.dotclockHz, cp.HTotal, cp.VTotal, cp.interlace); cp.horzHz = CalcHorizontalHz(cp.dotclockHz, cp.HTotal); DispCrtcParam(&cp); DisableGenerateSignal(); /* Unlock all */ SetSeqReg(0x08, 0x06); /* PLL Unlock */ /* VERT timing regs (CRTC index 6,7(bit0,2,3,5,7),9,10,11(bits0..3),15,16 ) */ /* HOR timing regs (CRTC index 0..5, 17(bit2) ) */ /* bit 0 of Clocking mode reg unlocked (8/9 dot font selection) */ /* Clock bits in MISC reg unlocked */ SetCrtcReg(0x35, GetCrtcReg(0x35) & 0x0F); SetCrtcReg(0x38, 0x48); /* S3 register set (CRTC index 0x30..0x3C) */ SetCrtcReg(0x39, 0xA5); /* system extension regs (CRTC index 0x50..0x5E) */ SetGenericVGARegister(&cp); SetS3ExtRegister(&cp); SetS3VClock(mul, _div, p); EnableGenerateSignal(); RegCRTCPARAM(&cp); return 1; } int setS3_15KHz(int vdouble, int width, int height) { return setS3(); } void centerS3_15KHz(int dx, int dy) { CRTCPARAM cp; GetCRTCPARAM(&cp); Centering(&cp, dx*8, dy); cp.HBStart = cp.HSStart; cp.HBEnd = cp.HTotal - 16; cp.VBStart = cp.VDisp; cp.VBEnd = cp.VTotal - 2; RegCRTCPARAM(&cp); SetGenericVGARegister(&cp); SetS3ExtRegister(&cp); } static void SetS3PLL(void) { BYTE d0; d0 = _in(0x3CC); d0 = d0 | 0x0C; _out(0x3C2, d0); } static void SetS3ExtRegister(CRTCPARAM *cp) { int d0; int d1; int HTotal; int HSStart; int HSEnd; HTotal = cp->HTotal >> 3; HSStart = cp->HSStart >> 3; HSEnd = cp->HSEnd >> 3; d1 = ((HTotal - 5) + HSStart) / 2; if((d1 - HSStart) < 4){ if ((HSStart + 4) <= (HTotal - 5)) d1 = HSStart + 4; else d1 = HTotal - 5 + 1; } d0 = 0; d0 = BitMov(d0, 0, (cp->HTotal >> 3) - 5, 8); d0 = BitMov(d0, 1, (cp->HDisp >> 3) - 1, 8); d0 = BitMov(d0, 2, (cp->HBStart >> 3) , 8); d0 = BitMov(d0, 4, (cp->HSStart >> 3) , 8); if ((cp->HBEnd >> 3) - (cp->HBStart >> 3) > 64) d0 = BitMov(d0, 3, 1, 0); if ((cp->HSEnd >> 3) - (cp->HSStart >> 3) > 32) d0 = BitMov(d0, 5, 1, 0); d0 = BitMov(d0, 6, d1, 8); SetCrtcReg(0x5D, d0); // ext horz reg SetCrtcReg(0x3B, d1); // data transfer exec pos logerror("s3: data transfer exec pos %d\n", d1 << 3); d0 = 0; d0 = BitMov(d0, 0, cp->VTotal - 2, 10); d0 = BitMov(d0, 1, cp->VDisp - 1, 10); d0 = BitMov(d0, 2, cp->VBStart, 10); d0 = BitMov(d0, 4, cp->VSStart, 10); d0 = BitMov(d0, 6, 1, 0); // line compare SetCrtcReg(0x5E, d0); // ext vert reg SetS3DoubleScan(cp->doublescan); SetS3Interlace(cp->interlace, cp); SetHVPolarity(cp->hpolarity, cp->vpolarity); } static void SetS3DoubleScan(int flag) { BYTE d0 = GetCrtcReg(0x09); if(flag == 0){ SetCrtcReg(0x09, d0 & 0xE0); }else{ SetCrtcReg(0x09, (d0 & 0xE0) | 0x80); } } static void SetS3Interlace(int flag, CRTCPARAM *cp) { int d1; if(flag == 0){ d1 = GetCrtcReg(0x42) & ~0x20; SetCrtcReg(0x42, d1); }else{ d1 = ((cp->HTotal >> 3) - 5) / 2; d1 = (d1 * cp->interlaceratio) / 100; SetCrtcReg(0x3C, d1); // interlace offset d1 = GetCrtcReg(0x42) | 0x20; SetCrtcReg(0x42, d1); } } /* 3C4h index 12h W(R/W): "Video PLL Data" (732/764) bit 0-4 N1. Frequency divider. Stored as 1-31, actual value 3-33 5-7 N2. Divides the frequency. 0: /1, 1: /2, 2: /4, 3: /8 8-14 M. Quotient. Stored as 1-127, actual value 3-129 Note: Frequency is (M/N1)/(1 << N2) *base frequency.M and N1 are the actual values, not the stored ones. Typically the base frequency is 14.318 MHz. */ static long GetS3VclkParam(long dotclock, int *s_mul, int *s_div, int *s_p) { int best_mul,best_div,best_p,best_clock,best_vco; int mul,_div,p; logerror("s3: clock range is %.2f - %.2f MHz\n", (double)s3_card->vco_min / 1000.0 / (1 << s3_card->p_max), (double)s3_card->vco_max / 1000.0 / (1 << s3_card->p_min) ); best_clock = -1; best_mul = 0; best_div = 0; best_p = 0; best_vco = 0; for(_div=s3_card->div_min;_div<=s3_card->div_max;++_div) for(p=s3_card->p_min;p<=s3_card->p_max;++p) for(mul=s3_card->mul_min;mul<=s3_card->mul_max;++mul) { int vco = s3_card->ref * 1000 * mul / _div; int new_clock = vco / (1 << p); if (vco < s3_card->vco_min*1000 && p!=s3_card->p_max) continue; if (vco > s3_card->vco_max*1000) continue; if (best_clock < 0 || abs(new_clock - dotclock) < abs(best_clock - dotclock)) { best_mul = mul; best_div = _div; best_p = p; best_clock = new_clock; best_vco = vco; } } if (best_clock < 0) return -1; if (best_vco < s3_card->vco_min*1000) logerror("s3: VCO clock %.2f is OUT of range %.2f - %.2f MHz\n", (double)best_vco / 1000000, (double)s3_card->vco_min / 1000, (double)s3_card->vco_max / 1000); else logerror("s3: VCO clock %.2f is in range %.2f - %.2f MHz\n", (double)best_vco / 1000000, (double)s3_card->vco_min / 1000, (double)s3_card->vco_max / 1000); *s_mul = best_mul; *s_div = best_div; *s_p = best_p; logerror("s3: ref:%.2f, mul:%d, div:%d, shift:%d\n",(double)s3_card->ref / 1000,best_mul,best_div,best_p); return best_clock; } static void SetS3VClock(int mul, int _div, int p) { unsigned d0; if (s3_card->clock_type == S3_CLOCK_TRIO) { unsigned SR12,SR13; SR12 = ((_div-2) & 0x1F) | (p & 0x7) << 5; SR13 = ((mul-2) & 0x7F); SetSeqReg(0x12, SR12); SetSeqReg(0x13, SR13); } else if (s3_card->clock_type == S3_CLOCK_VIRGEGX2) { unsigned SR12,SR13,SR29,ndiv; ndiv = ((_div-2) & 0x1F) | (p & 0x7) << 5; SR29 = ndiv >> 7; SR12 = (ndiv & 0x1f) | ((ndiv & 0x60) << 1); SR13 = ((mul-2) & 0x7F); SetSeqReg(0x12, SR12); SetSeqReg(0x13, SR13); SetSeqReg(0x29, SR29); } else if (s3_card->clock_type == S3_CLOCK_AURORA) { unsigned SR12,SR13; SR12 = ((_div-2) & 0x3F) | (p & 0x7) << 6; SR13 = ((mul-2) & 0x7F); SetSeqReg(0x12, SR12); SetSeqReg(0x13, SR13); } d0 = GetSeqReg(0x15) & ~0x21; d0 = d0 & ~(1 << 4); /* disable sequence divider */ SetSeqReg(0x15, d0 | 0x03); SetSeqReg(0x15, d0 | 0x23); SetSeqReg(0x15, d0 | 0x03); SetS3PLL(); }