home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1999 January
/
pcwk_01_1999.iso
/
Tajnepp
/
MCLK093
/
S3.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1997-05-05
|
34KB
|
1,141 lines
/* s3.cpp 05/04/97 0.93ß
*
* S3 class declarations
*
* As of current, support includes...
* S3-801/805 ( 4 settings, untested)
* S3-Vision864/866/868 ( 4 settings, partially tested)
* S3-Trio32/Trio64 ( 4 settings, untested)
*
* S3 SDAC MCLK programming ( non-chipset specific, should work any S3 )
* TI-VP3025/3026 MCLK programming ( only for 964/968 chipsets )
* S3 Trio (32/64/64V+) MCLK programming
* S3 Virge and VirgeVX MCLK programming (actually same as Trio MCLK)
*
* new in v0.83
* +created _S3::_868, _S3::_968 objects, primarily so the ::_info
* routine will correctly discriminate EDO/burst/1-cycle RAM
* v0.84 +fixed _S3 init code, so extended SR registers are unlocked
* +also added 2MCLK/3MCLK write control for S3 Trio chipsets
* v0.86 +fixed the S3 SDAC RAMDAC detection code...
* v0.87 +fixed the S3 SDAC detection code...REALLY! It should no
* longer hang non-SDAC cards.
* v0.88 +added MCLK routine for _Virge and _VirgeVX classes
* v0.90 cosmetic change to reported-text in _864::_fxn1()
* v0.92 cosmetic changes (no new code)
* v0.93 added code for TIVP3025 RAMDAC (S3 964/968 only)
* TIVP3025 code doesn't work yet
* 93b modified _s3::_s3() and added _s3::~_s3() to clean-up
* the modifications made to certain S3 registers
* modified TIVP3025/26 detection code
*/
#include "s3.h"
#include<dos.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<iostreams.h>
_S3::_S3( vga_info info ) : vga( info ) // Constructor
{
uchar DAC; // temp variable for special RAMDAC detection purposes
uchar temp; // scratch pad
save_cr38 = read_CR( 0x38 ); // Preserve register value
save_cr39 = read_CR( 0x39 ); // ...
save_cr45 = read_CR( 0x45 ); // ...
save_cr55 = read_CR( 0x55 ); // ...
save_sr08 = read_SR( 0x08 ); // ...
write_CR( 0x38, 0x48 ); // Unlock S3 VGA registers
write_CR( 0x39, 0xA5 ); // Unlock S3 801/805 registers
write_SR( 0x08, 0x06 ); // Unlock ext SR registers
sdac = FALSE;
tivpdac = FALSE;
// v0.86 The preceding two lines were added, to correctly force the
// following loop to operate correctly. The write-operation resets
// the SDAC's internal counter, so now we can read it exactly 4 times.
int i=0; // Initialize loop counter
do {
DAC = inportb( _DACmask );
temp = inportb( _DACmask );
} while ( temp != DAC && i++ < 255 );
// v0.87 added the i < 255 ... non-SDAC S3's should no longer hang
// in case DACmask was preloaded to return _DACID, we reset the
// counter (wait until consecutive read is equal to previous read)
if ( i < 255 ) {
i = 0;
do {
DAC = inportb( _DACmask );
} while ( temp == DAC && i++ < 255 );
}
if ( DAC == 0x70 || DAC == 0x73 ) { // S3 SDAC detected
if ( DAC == 0x70 )
strcat( id.chipset, " + SDAC (ID 0x70)" );
else
strcat( id.chipset, " + SDAC (ID 0x73)" );
sdac = TRUE; // S3 SDAC RAMDAC is present
}
if ( sdac == FALSE )
{ // Check for TIVP 3025 RAMDAC
// The TIVP 3025 RAMDAC's ID-byte is at indirect register 0x3F
// The indirect registers are accessed through "INDEX" and "DATA"
// RS[2:0]=110 "INDEX" , RS[2:0]=111 "DATA"
write_bit( _CRindex, 0x33, 4, 0 ); // Unlock DAC writes
write_bit( _CRindex, 0x55, 0, 1 ); // set DAC extension, RS2=1
write_bit( _CRindex, 0x55, 1, 0 ); // clear DAC extension, RS3=0
write_bit( _CRindex, 0x45, 5, 0 ); // set RS3/ODF to RS3
outportb( _DACmask, 0x3F ); // Write 0x3F to "INDEX" (RS[110])
DAC=inportb( _DACindexR ); // Read ID byte from "DATA" (RS[111])
outportb( _DACmask, 0xFF ); // Restore DACmask to 0xFF
if ( DAC == 0x25 )
{
tivpdac = TRUE;
strcat( id.chipset, " + TIVP3025" );
}
else if ( DAC == 0x26 ) // added TIVP3026 detection, v0.93b
{
tivpdac = TRUE;
strcat( id.chipset, " + TIVP3026" );
}
}
}
void
_S3::mclk_help( void ) // Display help for S3 DAC/ Trio MCLK programming
{
sprintf( msg.temp, "\nFormula for S3 MCLK driver... %s%s%s",
"(3 parameters, M, N, R)\n\t( M + 2 )\n\t--------- * 14.31818MHz",
"\n\t(N+2)*2^R\n\n\tConstraint: 135MHz < 2^R * 14.31818MHz < 270MHz",
"\n\t and... 1<=M<=127 1<=N<=31 0<=R<=3" );
}
_S3::~_S3() // Destructor added to clean-up MCLK's register fiddling
{
write_CR( 0x38, save_cr38 ); // Restore register value
write_CR( 0x39, save_cr39 ); // ... v0.93b
write_CR( 0x45, save_cr45 );
write_CR( 0x55, save_cr55 ); // ...
write_SR( 0x08, save_sr08 );
}
/* PLL
*
* Calculates MCLK frequency from M, N, _FREF, and R values
* returns double value MHz, works for S3 SDAC and S3Trio
*/
double
_S3::_PLL( uchar M, uchar N, uchar R )
{
double Rval;
switch ( R ) {
case 0: case 1: case 2: case 3:
Rval = pow( 2.0, R ); // Rval = 2 ^ R;
break; // <- how'd I forget to put this here... //
default:
Rval=0; // Error!
}
return ( ( M + 2.0 ) * _OSC ) / ( ( N + 2.0 ) * Rval );
/* ( M + 2 )
* --------- * _FREF ... _FREF = 14.31818 MHz
* (N+2)*2^R
*
* constraint: 135MHz < 2^R * _FREF < 270MHz
* and... N >= 1
*/
}
message
_S3::_ramtype( uchar mask )
{ // mask determines which values are valid, i.e. not "RESERVED"
uchar _CR36 = read_CR( 0x36 );
_CR36 = ( _CR36 >> 2 ) & 0x03; // 7654 3210 -> 0000 0032, bits 3-2
switch ( _CR36 ) {
case 0: if ( ( mask & 0x01 ) ) // If bit0 is SET
sprintf( msg.text, "1-cycle EDO RAM" );
else
sprintf( msg.text, "RESERVED [ CR36(3:2) = 00 ]" );
break;
case 1: if ( ( mask & 0x02 ) ) // If bit1 is SET
sprintf( msg.text, "Burst-mode RAM" );
else
sprintf( msg.text, "RESERVED [ CR36(3:2) = 01 ]" );
break;
case 2: sprintf( msg.text, "2-cycle EDO RAM" );
break;
case 3: if ( ( mask & 0x08 ) ) // If bit 3 is SET
sprintf( msg.text, "fast-page mode RAM");
else
sprintf( msg.text, "RESERVED [ CR36(3:2) = 04 ]" );
break;
default: sprintf( msg.text, "UNKNOWN [ 0x%02X ]", mask );
}
status = EXIT_SUCCESS;
return msg;
}
void
_S3::_mclk( int cmd ) // MCLK programming for S3 SDAC chips
{
if ( cmd == _QUERY && sdac == TRUE ) {
sprintf( msg.text,"0 S3 SDAC MCLK programming\n" );
return; }
else if ( sdac == FALSE ) {
sprintf( msg.text,"0 MCLK function NOT available\n" );
return;
}
uchar _CR33 = read_bit( _CRindex, 0x33, 4 ); // Remember LOCK DACW v0.84
uchar _M, _N, _R, byte1, byte2;
write_bit( _CRindex, 0x55, 0, 1 ); // Enable DAC extension RS2
outportb( _DACindexR, 0x0A ); // Set to PLL CLK1 parameter
byte1 = inportb( _DACIO );
byte2 = inportb( _DACIO );
_M = byte1 & 0x7F;
_N = byte2 & 0x1F;
_R = ( byte2 >> 5 ) & 0x03;
sprintf( msg.text,"Old MCLK = %.2fMHz ( M=%u, N=%u, R=%u ) ",
_PLL( _M, _N, _R ), _M, _N, _R );
if ( num_param < 3 && cmd == _SET ) {
strcat( msg.text, "\n...not enough parameters, need total of 3." );
cmd = _HELP; } // Not enough parameters. }
else {
_M = (uchar)atoi( param[ 0 ] ) & 0x7F;
_N = (uchar)atoi( param[ 1 ] ) & 0x1F ;
_R = (uchar)atoi( param[ 2 ] );
byte1 = ( byte1 & 0x80 ) | _M;
_R = ( _R << 5 ) & 0X60;
byte2 = ( byte2 & 0x80 ) | _N | _R;
}
switch ( cmd ) {
case _SET: write_bit( _CRindex, 0x33, 4, 0 );
// enable RAMDAC writes
write_bit( _CRindex, 0x55, 0, 1 ); // actv RS2
outportb( _DACindexW, 0x0A ); // Set to PLL CLK1
outportb( _DACIO, byte1 ); // Write 1st byte
outportb( _DACIO, byte2 ); // Write 2nd byte
write_bit( _CRindex, 0x55, 0, 1 );
// Enable DAC extension RS2
outportb( _DACindexR, 0x0A ); // Set to PLL CLK1 parameter
byte1 = inportb( _DACIO );
byte2 = inportb( _DACIO );
_M = byte1 & 0x7F;
_N = byte2 & 0x1F;
_R = ( byte2 >> 5 ) & 0x03;
sprintf(msg.temp,"\nNew MCLK = %.2fMHz ( M=%u, N=%u, R=%u ) ",
_PLL( _M, _N, _R ), _M, _N, _R );
strcat( msg.text, msg.temp );
outportb( _DACIO, byte1 );
write_bit( _CRindex, 0x33, 4, _CR33 ); // Restore bit4
break;
case _GET: case _HELP:
strcat( msg.text, "\nS3 SDAC MCLK programming" );
mclk_help(); // Get mclk help, put in msg.temp
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_S3::_mclk(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_S3::_fxn1( int cmd ) // WST CTL, EWRT POST Write-wait state control
{
if ( cmd == _QUERY ) {
sprintf( msg.text,"1 S3-801/805 WST CTL, EWRT POST\n%s",
" (2 items : write-wait state, write-buffer controls)\n" );
return;
}
uchar _bit2 = read_bit( _CRindex, 0x40, 2 ),
_bit3 = read_bit( _CRindex, 0x40, 3 );
sprintf(msg.text,"Old WST-CTL wait-states = %uT, buffer = %sd",_bit2,
bitstat( _bit3 ) );
if ( num_param < 2 && cmd == _SET ) {
strcat( msg.text, "\nError! TWO parameters required!");
cmd = _HELP; }
else if ( num_param >= 2 ) {
_bit2 = ( (uchar)atoi( param[ 0 ] ) ) & 0x01;
_bit3 = ( (uchar)atoi( param[ 1 ] ) ) & 0x01;
}
switch ( cmd ) {
case _SET:
write_bit( _CRindex, 0x40, 2, _bit2 );
write_bit( _CRindex, 0x40, 3, _bit3 );
sprintf( msg.temp,"\nNew WST-CTL = %uT, buffer = %sd", _bit2,
bitstat( _bit3 ) );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "\nWrite Wait State Control\n\t%s%s",
"0 = no wait\n\t1 = one wait-state\nEnable Fast-Write ",
"buffer\n\t0 = DISABLE\n\t1 = ENABLE");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_S3::_fxn1(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_S3::_fxn2( int cmd ) // Decode wait control, 386/486 local bus
{ // bits 5-4 of _CR40
if ( cmd == _QUERY ) {
strcpy( msg.text, "2 S3-805 Decode wait control (local bus)\n" );
return;
}
uchar _CR40 = ( read_CR( 0x40 ) >> 4 ) & 0x03 ;
uchar new_CR40= 0x02; // 3 wait states, default
if ( param[ 0 ] != NULL )
new_CR40 = (uchar)atoi( param[ 0 ] );
sprintf( msg.text, "Old Decode = %uT wait-states (WS) ", shift (_CR40 ));
switch ( cmd ) {
case _SET: _CR40 = read_CR ( 0x40 ) & 0xCF; // XX00 XXXX
write_CR( 0x40, _CR40 | ( ( new_CR40 << 4 ) & 0x30 ) );
sprintf(msg.temp,"\n...now set to %uT WS ",shift(new_CR40 ));
strcat( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp,"\nDecode wait Control (local bus only)%s",
"\n\t0 = 0WS\n\t1 = 1WS\n\t2 = 3WS\n\t3 = 2WS");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_S3::_fxn3(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_S3::_fxn3( int cmd ) // Read Wait control
{ // bits 7-6 of _CR40
if ( cmd == _QUERY ) {
strcpy( msg.text,
"3 S3-801/805 Read-wait state control (local bus/ISA)\n" );
return;
}
uchar _CR40 = ( read_CR( 0x40 ) >> 6 ) & 0x03;
uchar new_CR40= 0x02; // 3 wait states, default
if ( param[ 0 ] != NULL )
new_CR40 = (uchar)atoi( param[ 0 ] );
sprintf( msg.text, "Old Read Wait Control = %u waitstates (WS) ",
shift (_CR40 ) );
switch ( cmd ) {
case _SET: _CR40 = read_CR ( 0x40 ) & 0x3F; // 00XX XXXX
write_CR( 0x40, _CR40 | ( ( new_CR40 << 6 ) & 0xC0 ) );
sprintf(msg.temp,"\nNow set to %uT WS ", shift ( new_CR40 ));
strcat( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp,"\nRead Wait Control\n\t%s",
"0 = 0WS\n\t1 = 1WS\n\t2 = 3WS\n\t3 = 2WS");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_S3::_fxn3(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_S3::_fxn4( int cmd ) // Read-ahead cache
{ // bits 5-4 of _CR40
if ( cmd == _QUERY ) {
strcpy( msg.text, "4 S3-801/805 Read ahead cache size\n" );
return;
}
uchar _CR54 = read_CR( 0x54 ) & 0x07 ;
uchar new_CR54= 0x07; // Maximum allowed
if ( param[ 0 ] != NULL )
new_CR54 = (uchar)atoi( param[ 0 ] );
sprintf( msg.text, "Old Cache size set to %u accesses, and %sd", _CR54,
bitstat( read_bit( _CRindex, 0x58, 2 ) ) );
switch ( cmd ) {
case _SET: _CR54 = read_CR ( 0x54 ) & 0xF8; // XXXX X000
write_CR( 0x54, _CR54 | ( new_CR54 & 0x07 ) );
sprintf(msg.temp,"\nCache set to %u accesses", read_CR( 0x54)
& 0x07 ) ;
write_bit( _CRindex, 0x58, 2, 1 ); // Enable read-ahead
strcat( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp,"\nRead ahead cache (auto-enabled if %s",
"modified)\n\tAcceptable values = 0, 1, 3, 7");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_S3::_fxn3(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
//--------------------S3 805i class definitions---------------------------
void
_805i::_fxn5( int cmd ) // Memory interleave control
{ // bit5 of _CR53, memory interleave control
if ( cmd == _QUERY ) {
strcpy( msg.text,"5 S3-805i memory interleave (2mb only)\n");
return;
}
uchar new_CR53 = 0x0; // Disable interleaving by default
if ( param[ 0 ] != NULL )
new_CR53 = ( (uchar)atoi( param[ 0 ] ) ) & 0x01;
sprintf(msg.text,"Interleaving is %sd",
bitstat( read_bit( _CRindex, 0x53, 5 ) ) );
switch ( cmd ) {
case _SET:
write_bit( _CRindex, 0x53, 5, new_CR53 );
sprintf( msg.temp, "\n...now %sd",
bitstat ( read_bit( _CRindex, 0x53, 5 ) ) );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "\nMemory interleaving control\n\t%s",
"0 = disabled\n\t1 = enabled (requires 2mb video RAM)");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_805i::_fxn5(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
//---------- S3 '64 and Trio chipsets -------------------------------------//
message
_864::_info( void )
{
return _ramtype( 0x0C ) ;
// Call _ramtype, allowing case2 & 3
}
/*
void
_864::_fxn1( int cmd ) // Ready Control delay (VL-BUS only )
{ // bit4 of _CR40
if ( cmd == _QUERY ) {
strcpy( msg.text,
"1 S3-864/964/Trio RDY CTL wait control (VL-bus only)\n");
return;
}
uchar _CR40 = read_bit( _CRindex, 0x40, 4 );
uchar new_CR40= 0x01;
if ( param[ 0 ] != NULL )
new_CR40 = ( (uchar)atoi( param[ 0 ] ) ) & 0x01;
sprintf(msg.text,"Old RDY-CTL = %uT wait state(s) ", _CR40 );
switch ( cmd ) {
case _SET:
write_bit( _CRindex, 0x40, 4, new_CR40 );
sprintf( msg.temp,"\nNew RDY-CTL=%uT", new_CR40 );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "\nVL-bus RDY Control delay\n\t%s",
"0T no-wait = 0d\n\t1T One-wait= 1d");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_864::_fxn1(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
*/
void
_864::_fxn1( int cmd ) // Memory page mode control )
{ // bits3-2 of _CR36
if ( cmd == _QUERY ) {
strcpy( msg.text,
"1 S3-86x/96x/Trio memory page-mode control\n");
return;
}
uchar _CR36 = read_CR( 0x36 ) & 0xF3; // XXXX 00XX
uchar new_CR36 = _CR36 & 0x03;
if ( param[ 0 ] != NULL )
new_CR36 = ( (uchar)atoi( param[ 0 ] ) ) & 0x03;
switch ( cmd ) {
case _SET:
write_CR( 0x36, ( new_CR36 << 2 ) | _CR36 );
sprintf( msg.temp,"\nSelected RAM access-mode %u", new_CR36 );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "S3 86x/96x/Trio access-mode control%s%s%s",
"\n\t0 = 1-cycle EDO (866/868/968)\n\t1 = burst DRAM ",
"(866/868/968)\n\t2 = 2-cycle EDO",
"\n\t3 = fast-page mode timing");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_864::_fxn1(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_864::_fxn2( int cmd ) // Ready Control delay (VL-BUS only )
{ // bits 1-0 of _CR68
if ( cmd == _QUERY ) {
strcpy( msg.text,
"2 S3-864/964/Trio CAS', OE' strech time, WE' delay\n" );
return;
}
uchar _CR68 = read_CR( 0x68 ) & 0x03;
uchar new_CR68= 0x0; // maximum delay default
if ( param[ 0 ] != NULL )
new_CR68 = ( (uchar)atoi( param[ 0 ] ) ) & 0x03;
sprintf(msg.text,"Old stretch/delay value = %02X (see table) ", _CR68 );
switch ( cmd ) {
case _SET: _CR68 = read_CR( 0x68 ) & 0xFC; // XXXX XX00
write_CR( 0x68, _CR68 | new_CR68 );
sprintf( msg.temp,"\nNew delay value = %02X ", new_CR68 );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "\nDRAM timing delays/stretches%s",
"\n\t0 = 6.5ns\n\t1 = 5ns\n\t2 = 3.5ns\n\t3 = 0ns");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_864::_fxn2(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
// Does not work for 86X/Trio chipsets!!! Timing MUST be set to RDYIN'!
/*
void
_864::_fxn3( int cmd ) // Write latching delay (VL-BUS only )
{ // bit5 of _CR40, 1= SRDY', 0 = RDYIN'
if ( cmd == _QUERY ) {
strcpy( msg.text,
"3 S3-864/964 Write latching delay (VL-bus only)\n");
return;
}
uchar _CR40 = read_bit( _CRindex, 0x40, 5 );
uchar new_CR40= 0x01;
sprintf( msg.text,"Old Write latch delay = (%ud, latch on ",_CR40 );
if ( _CR40 )
strcat( msg.text, "SRDY' )" );
else
strcat( msg.text, "RDYIN' )" );
if ( param[ 0 ] != NULL )
new_CR40 = (uchar)atoi( param[ 0 ] );
switch ( cmd ) {
case _SET:
write_bit( _CRindex, 0x40, 5, new_CR40 );
sprintf(msg.temp,"\nNew Write latch delay=%ud ", new_CR40 );
if ( new_CR40 )
strcat( msg.temp, "SRDY' )" );
else
strcat( msg.temp, "RDYIN' )" );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "\nVL-bus Write latch delay\n\t%s",
"RDYIN' = 0d\n\tSRDY' = 1d (faster?)");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_864::_fxn3(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
} */
void
_864::_fxn3( int cmd ) // Bus turnaround time
{ // bits7-6 of _CR40
if ( cmd == _QUERY ) {
strcpy( msg.text,
"3 S3-864/964 Bus turnaround non-overlap (VL-bus only)\n");
return;
}
uchar _CR40 = ( read_CR( 0x40 ) >> 6 ) & 0x03,
new_CR40;
sprintf( msg.text,"Old delay = %u unit(s) ", _CR40 + 1 );
if ( param[ 0 ] != NULL )
new_CR40 = (uchar)atoi( param[ 0 ] ) & 0x03;
switch ( cmd ) {
case _SET: _CR40 = read_CR( 0x40 ) & 0x3F;
write_CR( 0x40, ( ( new_CR40 >> 6 ) & 0xC0 ) | _CR40 );
sprintf( msg.temp,"\nNew delay = %u unit(s)", new_CR40 + 1 );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "\nVL-bus gap between ABEN' & DBEN'%s%s",
"\n\t1 unit = 00\n\t2 units = 01\n\t3 units = 02",
"\n\t4 units = 03");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_864::_fxn3(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_864::_fxn4( int cmd ) // RAS precharge timing select
{ // bits 7-6 of _CR68
if ( cmd == _QUERY ) {
strcpy( msg.text, "4 S3-864/964 RAS' precharge timing delay\n" );
return;
}
uchar _CR68 = ( read_CR( 0x68 ) >> 6 ) & 0x03; // 7654 3210 -> 0000 0076
uchar new_CR68= 0x01; // maximum delay default, 4.5 MCLKs
if ( param[ 0 ] != NULL )
new_CR68 = ( (uchar)atoi( param[ 0 ] ) ) & 0x03;
sprintf(msg.text,"Old RAS' precharge value = %02X (see table) ", _CR68 );
switch ( cmd ) {
case _SET: _CR68 = read_CR( 0x68 ) & 0x3F; // 00XX XXXX
write_CR( 0x68, _CR68 | ( ( new_CR68 << 6 ) & 0xC0 ) );
sprintf( msg.temp,"\nNew RAS' value = %02X ", new_CR68 );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "\nDRAM RAS' precharge delays%s",
"\n\t0 = RESERVED\n\t1 = 4.5MCLKs\n\t2 = 3.5\n\t3 = 2.5");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_864::_fxn4(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_864::_fxn5( int cmd ) // RAS precharge timing select
{ // bits 5-4 of _CR68
if ( cmd == _QUERY ) {
strcpy( msg.text, "5 S3-864/964 RAS' low timing select\n" );
return;
}
uchar _CR68 = ( read_CR( 0x68 ) >> 4 ) & 0x03; // 7654 3210 -> 0000 0054
uchar new_CR68= 0x00; // maximum delay default, 6.5 MCLKs
if ( param[ 0 ] != NULL )
new_CR68 = ( (uchar)atoi( param[ 0 ] ) ) & 0x03;
sprintf(msg.text,"Old RAS' low timing value = %02X (see table) ", _CR68 );
switch ( cmd ) {
case _SET: _CR68 = read_CR( 0x68 ) & 0xCF; // XX00 XXXX
write_CR( 0x68, _CR68 | ( ( new_CR68 << 4 ) & 0x30 ) );
sprintf( msg.temp,"\nNew RAS' value = %02X", new_CR68 );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "\nDRAM RAS' low timing select%s",
"\n\t0 = 6.5MCLKs\n\t1 = 5.5\n\t2 = 4.5\n\t3 = 3.5");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_864::_fxn5(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
message
_868::_info( void )
{
return _ramtype( 0x0F ) ;
// Call _ramtype, allowing all cases 0, 1, 2 & 3
}
void // Read "M, N, R" values from S3 registers _SR10 & _SR11
_Trio::read_PLL( uchar *M, uchar *N, uchar *R )
{
uchar _SR10= read_SR( 0x10 ); // Read _SR10, contains N & R
*M = read_SR( 0x11 ) & 0x7F; // _SR11 -> 0XXX XXXX = M value
*N = _SR10 & 0x1F; // _N = _SR11 -> 000X XXXX
*R = ( _SR10 >> 5 ) & 0x03; // _R ( 7654 3210 -> 0000 0065 )
}
message
_Triov::_info( void )
{
return _ramtype( 0x0D ) ;
// Call _ramtype, allowing case0, 2 & 3
}
void
_Trio::_mclk( int cmd ) // MCLK reprogramming
{ // _SR10, _SR11
if ( cmd == _QUERY ) {
strcpy( msg.text, "0 S3 Trio/Virge MCLK programming\n");
return;
}
uchar _M, _N, _R, _SR10, _SR11;
read_PLL( &_M, &_N, &_R ); // Load _M, _N, _R with PLL values
sprintf( msg.text,"Old MCLK = %.2fMHz ( M=%u, N=%u, R=%u ) ",
_PLL( _M, _N, _R ), _M, _N, _R );
if ( num_param < 3 && cmd == _SET ) {
strcat( msg.text, "\n...not enough parameters, need total of 3." );
cmd = _HELP; } // Not enough parameters. }
else {
_M = (uchar)atoi( param[ 0 ] );
_N = (uchar)atoi( param[ 1 ] );
_R = (uchar)atoi( param[ 2 ] );
_M = ( _M & 0x7F );
_SR11 = ( read_SR( 0x11 ) & 0x80 ) | _M;
_N = ( _N & 0x1F );
_R = ( _R << 5 ) & 0X60 ;
_SR10 = ( read_SR( 0x10 ) & 0x80 ) | _N | _R;
}
switch ( cmd ) {
case _SET: write_bit( _SRindex, 0x15, 0, 0 ); // Prep MCLK load
write_SR( 0x11, _SR11 );
write_SR( 0x10, _SR10 );
write_bit( _SRindex, 0x15, 0, 1 ); // Load MCLK values
delay(1000); // Wait for new values to take effect
write_bit( _SRindex, 0x15, 0, 0 ); // Clear MCLK load
read_PLL( &_M, &_N, &_R); // Reread new values
sprintf(msg.temp,"\nNew MCLK = %.2fMHz ( M=%u, N=%u, R=%u ) ",
_PLL( _M, _N, _R ), _M, _N, _R );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
strcat( msg.text, "\nS3 Trio/Virge MCLK programming" );
mclk_help(); // Get Trio mclk help, put in msg.temp
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_Trio::_mclk(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_Trio::_fxn3( int cmd ) // RAS' timing control
{ // bit2 = RAS' Low, bit3 = RAS' precharge _CR68
if ( cmd == _QUERY ) {
strcpy(msg.text,"3 S3Trio RAS' timing control (2 parameters)\n");
return;
}
uchar _bit2 = read_bit( _CRindex, 0x68, 2 ),
_bit3 = read_bit( _CRindex, 0x68, 3 );
sprintf( msg.text, "Old RAS-LOW = %.1fMCLKs, Old RAS-Pre = %.1fMCLKs",
4.5 - _bit2, 3.5 - _bit3 );
if ( num_param < 2 && cmd == _SET ) {
strcat( msg.text, "\nError! Need TWO parameters for input!");
cmd = _HELP; }
else if ( num_param >= 2 ) {
_bit2 = ( (uchar)atoi( param[ 0 ] ) ) & 0x01;
_bit3 = ( (uchar)atoi( param[ 1 ] ) ) & 0x01;
}
switch ( cmd ) {
case _SET: write_bit( _CRindex, 0x68, 2, _bit2 );
write_bit( _CRindex, 0x68, 3, _bit3 );
sprintf(msg.temp,"\nNew LOW = %.1fMCLKs, New PRE = %.1fMCLKs",
4.5 - _bit2, 3.5 - _bit3 );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "\nDRAM RAS delays/stretches%s%s",
"\n\tRAS'LOW RAS'PRE (1st-param 2nd-param )",
"\n\t0 = 4.5 0 = 3.5\n\t1 = 3.5 1 = 2.5" );
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_Trio::_fxn3(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_Trio::_fxn4( int cmd ) // Memory write timing, 2MCLK/3MCLK, SR08/SR15
{
if ( cmd == _QUERY ) {
sprintf( msg.text,"4 S3Trio 2MCLK/3MCLK timing controls\n" );
return;
}
uchar _SR0A = read_bit( _SRindex, 0x0A, 7 ), // SR0A/b7 = CPU-write
_SR15 = read_bit( _SRindex, 0x15, 7 ); // SR15/b7 = mem-write
sprintf(msg.text,"Old CPU-write = %uMCLKs, mem-write = %uMCLKS",
3 - _SR0A, 3 - _SR15 );
if ( num_param < 2 && cmd == _SET ) {
strcat( msg.text, "\nError! TWO parameters required!");
cmd = _HELP; }
else if ( num_param >= 2 ) {
_SR0A = ( (uchar)atoi( param[ 0 ] ) ) & 0x01;
_SR15 = ( (uchar)atoi( param[ 1 ] ) ) & 0x01;
}
switch ( cmd ) {
case _SET:
write_bit( _SRindex, 0x0A, 7, _SR0A );
write_bit( _SRindex, 0x15, 7, _SR15 );
sprintf( msg.temp,"\nNew CPU = %uMCLKs, mem = %uMCLKs",
3 - _SR0A, 3 - _SR15 );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "\nCPU-write and MEM-write control\n%s%s%s",
"For each item, enter: \n\t0 (3MCLKs)\n\t1 (2MCLKs, faster,",
" but MCLK freq must be < 57MHz.)\n\tAdditional constraint ",
"for 2MCLK MEM-write: 55MHz < MCLK < 57MHz" );
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_Trio::_fxn4(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_Triov::_fxn1( int cmd ) // Memory page mode control )
{ // bits3-2 of _CR36
if ( cmd == _QUERY ) {
strcpy( msg.text,
"1 Trio64V+/Virge memory page-mode control\n");
return;
}
uchar _CR36 = read_CR( 0x36 ) & 0xF3; // XXXX 00XX
uchar new_CR36 = _CR36 & 0x03;
if ( param[ 0 ] != NULL )
new_CR36 = ( (uchar)atoi( param[ 0 ] ) ) & 0x03;
switch ( cmd ) {
case _SET:
write_CR( 0x36, ( new_CR36 << 2 ) | _CR36 );
sprintf( msg.temp,"\nSelected RAM access-mode %u", new_CR36 );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "S3 Trio64V+/Virge memory access-mode%s%s",
" control\n\t0 = 1-cycle EDO\n\t2 = 2-cycle EDO \n\t",
"3 = fast-page mode timing");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_Triov::_fxn1(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
message
_964::_info( void )
{
return ( _ramtype( 0x0C ) );
// Call _ramtype, allowing only case2 & 3
}
/* PLL - TIVP3025
*
* Calculates MCLK frequency from M, N, _FREF, and P values
* returns double value MHz, works for TIVP3025 RAMDAC
*/
double
_964::_PLL( uchar M, uchar N, uchar P )
{
double Pval;
switch ( P ) {
case 0: case 1: case 2: case 3:
Pval = pow( 2.0, P ); // Pval = 2 ^ P;
break; // <- how'd I forget to put this here...
default:
Pval=0; // Error!
}
return ( 8.0 * ( M + 2.0 ) * _OSC ) / ( ( N + 2.0 ) * Pval );
/* ( M + 2 )
* FVCO = --------- * 8 * _FREF ... _FREF = 14.31818 MHz
* ( N + 2 )
*
* FPLL (MHz) = FVCO / ( 2^P ) FPLL = final MCLK frequency
*
* constraint: 110MHz < FVCO < 220MHz
* and... FREF/(N+2) > 0.5MHz
* and... N >= 1 , M >= 1
*/
}
void
_964::_mclk( int cmd ) // MCLK programming for TI VP3025 RAMDAC
{
if ( cmd == _QUERY && tivpdac == TRUE ) {
sprintf( msg.text, "0 TIVP3025/3026 RAMDAC MCLK programming\n" );
return; }
else if ( tivpdac == FALSE ) {
sprintf( msg.text,"0 MCLK function NOT available\n" );
return;
}
uchar _CR33 = read_bit( _CRindex, 0x33, 4 ); // Remember LOCK DACW v0.84
uchar _M, _N, _P, byte1, byte2, byte3;
write_bit( _CRindex, 0x55, 0, 1 ); // Enable DAC extension RS2
write_bit( _CRindex, 0x55, 1, 0 ); // Disable DAC extension RS3
// TIVP3025's indirect registers are accessed via "INDEX" and "DATA"
// where RS[2:0]=110 "INDEX" , RS[2:0]=111 "DATA"
// MCLK PLL Control register is at 0x2E
//
outportb( _DACmask, 0x2E ); // set "INDEX" (RS[110]) to MCLK PLL-con
// To access first MCLK PLL register, must write 00 -> PLLcontrol[1:0]
write_bit( _DACindexR, 0, 0 ); // 0 -> bit0 of PLLcontrol
write_bit( _DACindexR, 1, 0 ); // 0 -> bit1 of PLLcontrol
// PLL control register is autoincremented
byte1 = inportb( _DACmask ); // Read data from MCLK PLL data register
byte2 = inportb( _DACmask ); // ...
byte3 = inportb( _DACmask ); // ...
_N = byte1 & 0x7F;
_M = byte2 & 0x7F;
_P = byte3 & 0x03;
sprintf( msg.text,"Old MCLK = %.2fMHz ( M=%u, N=%u, P=%u ) ",
_PLL( _M, _N, _P ), _M, _N, _P );
if ( num_param < 3 && cmd == _SET ) {
strcat( msg.text, "\n...not enough parameters, need total of 3." );
cmd = _HELP; } // Not enough parameters. }
else {
_M = (uchar)atoi( param[ 0 ] ) & 0x7F;
_N = (uchar)atoi( param[ 1 ] ) & 0x7F;
_P = (uchar)atoi( param[ 2 ] ) & 0x03;
byte1 = ( byte1 & 0x80 ) | _M;
byte2 = ( byte2 & 0x80 ) | _N;
byte3 = ( byte3 & 0xFC ) | _P;
}
switch ( cmd ) {
case _SET: write_bit( _CRindex, 0x33, 4, 0 );
// enable RAMDAC writes
write_bit( _CRindex, 0x55, 0, 1 ); // set RS2 to 1
write_bit( _CRindex, 0x55, 1, 0 ); // clear RS3 to 0
outportb( _DACmask, 0x2E ); // set "INDEX" to 0x2E
write_bit( _DACindexR, 0, 0 ); // 0 -> bit0 of PLLcontrol
write_bit( _DACindexR, 1, 0 ); // 0 -> bit1 of PLLcontrol
outportb( _DACmask, byte1 ); // Write 1st byte
outportb( _DACmask, byte2 ); // Write 2nd byte
outportb( _DACmask, byte3 ); // Write 3rd byte
// Now, read-back the new values!
outportb( _DACmask, 0x2E ); // set "INDEX" to 0x2E
write_bit( _DACindexR, 0, 0 ); // 0 -> bit0 of PLLcontrol
write_bit( _DACindexR, 1, 0 ); // 0 -> bit1 of PLLcontrol
byte1 = inportb( _DACmask ); // Read data from MCLK PLL
byte2 = inportb( _DACmask ); // ...
byte3 = inportb( _DACmask ); // ...
_N = byte1 & 0x7F;
_M = byte2 & 0x7F;
_P = byte3 & 0x03;
sprintf(msg.temp,"\nNew MCLK = %.2fMHz ( M=%u, N=%u, P=%u ) ",
_PLL( _M, _N, _P ), _M, _N, _P );
strcat( msg.text, msg.temp );
write_bit( _CRindex, 0x33, 4, _CR33 ); // Restore bit4
break;
case _GET: case _HELP:
strcat( msg.text, "\nTIVP3025 RAMDAC MCLK programming" );
mclk_help(); // Get mclk help, put in msg.temp
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_964::_mclk(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_964::mclk_help( void ) // Display help for TIVP3025 MCLK programming
{
sprintf( msg.temp, "\nFormula for TI VP3025 MCLK driver... %s%s%s",
"(3 parameters, M, N, P)\n\t( M + 2 ) * 8\n\t--------- * 14.31818MHz",
"\n\t(N+2)*2^P\n\n\tConstraint: 110MHz < MCLK * (2^P) < 220MHz",
"\n\t and... 1<=M<=127 1<=N<=127 0<=P<=3" );
}
void
_964::_fxn1( int cmd ) // Ready Control delay (VL-BUS only ) & SAM
{ // bit4 of _CR40 and bit6 of CR58
if ( cmd == _QUERY ) {
strcpy( msg.text,
"1 S3-964 RDY CTL (VL-bus only), VRAM SAM control (2 items)\n");
return;
}
uchar _CR40 = read_bit( _CRindex, 0x40, 4 ),
_CR58 = read_bit( _CRindex, 0x58, 6 );
uchar new_CR40=0, new_CR58=0;
if ( num_param < 2 && cmd == _SET ) {
strcat( msg.text, "\nError! TWO parameters required!");
cmd = _HELP; }
else if ( num_param >= 2 ) {
new_CR40 = ( (uchar)atoi( param[ 0 ] ) ) & 0x01;
new_CR58 = ( (uchar)atoi( param[ 1 ] ) ) & 0x01;
}
sprintf(msg.text,"Old RDY-CTL = %uT wait state(s), SAM = %d", _CR40,
512 - ( 256 * _CR58 ) );
switch ( cmd ) {
case _SET:
write_bit( _CRindex, 0x40, 4, new_CR40 );
write_bit( _CRindex, 0x58, 6, new_CR58 );
sprintf( msg.temp,"\nNew RDY-CTL = %uT, SAM = %d", new_CR40,
512 - ( 256 * new_CR58 ) );
strcat ( msg.text, msg.temp );
break;
case _HELP: case _GET:
sprintf( msg.temp, "\nVL-bus RDY Control delay\n\t%s%s",
"0 = no-wait\n\t1 = one wait-state\n\n\tSAM control",
"\n\t0 = 512 words ( faster )\n\t1 = 256 words");
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_964::_fxn1(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
message
_968::_info( void )
{
return _ramtype( 0x0D );
// Call _ramtype, allowing cases 0, 2 & 3
}
//----S3 Virge
message
_Virge::_info( void )
{
return _ramtype( 0x05 ) ;
// Call _ramtype, allowing case0 & 2
return msg;
}
void
_VirgeVX::mclk_help( void ) // Display help for VirgeVX MCLK programming
{
sprintf( msg.temp, "\nFormula for S3 Virge/VX MCLK driver... %s%s%s",
"(3 parameters, M, N, R)\n\t( M + 2 )\n\t--------- * 14.31818MHz",
"\n\t(N+2)*2^R\n\n\tConstraint: 220MHz < 2^R * 14.31818MHz < 440MHz",
"\n\t and... 1<=M<=127 1<=N<=31 0<=R<=3" );
}