home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1999 January
/
pcwk_01_1999.iso
/
Tajnepp
/
MCLK093
/
CIRRUS.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1997-04-02
|
19KB
|
648 lines
/* cirrus.cpp 03/21/97 0.93α
*
* Includes class definitions for Cirrus Logic
* GD-5420/22 (_cirrus) 1 setting, tested
* GD-5424 and up ( 3 settings, tested...4th setting is read-back only)
* GD-543x ( 3 settings, only the 1st one is tested )
*
* v0.83B, GD-5434 MCLK will warn user about GD-5434 BIOS
* v0.83B, tries to get Cirrus Silicon revision (through BIOS 0x10)
* v0.84B, Cirrus Logic GD-5436 specific code
* (GD5430/5440 grouped together)
* v0.85B, converted sprintf calls to ostrstream << method
* v0.89B, modified 5436 functions to read "5436/46"
* v0.92B, cosmetic changes (no new code)
* v0.93α, added Cirrus Logic GD-546X code (untested), more cosmetic changes
*/
#include "cirrus.h"
#include<dos.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<iostreams.h>
/*
* cirrus.h
* cirrus class function definitions
*/
message
_cirrus::_info( void )
{
INITMSG( msg.text );
union REGS reg;
reg.h.ah = 0x12 ;
reg.h.bl = 0x80 ;
int86( 0x10, ®, ® );
if ( reg.h.bl != 0x80 ) {
msgout << "Chip revision = " ;
hexout( msgout, reg.h.bl ); // print reg.h.bl in "XX" format
} else
msgout << "Chip revision NOT available.";
msgout << " RAS timing mode = ";
if ( read_bit( _SRindex, 0x0F, 2 ) )
msgout << "standard (faster) ";
else
msgout << "extended (slower) ";
msgout << ends ;
return msg;
}
void
_cirrus::_mclk( int cmd )
{
uchar _SR1F = read_SR ( 0x1F ) & 0x3F; // Read _SR1F register
uchar new_MCLK = _SR1F; // New MCLK byte
INITMSG( msg.text );
if ( cmd == _QUERY ) {
msgout << "0 GD-54xx MCLK memory clock setting\n" << ends;
return;
}
// sprintf( msg.text, "Current MCLK = %.2fMHz (%02dd)", get_mclkfreq(),
// _SR1F );
msgout.precision( 2 );
msgout << "Old MCLK = " << get_mclkfreq() << "MHz (" << (int)(_SR1F)
<< "d)";
if ( param[ 0 ] != NULL ) // Only the 1st parameter is used
new_MCLK = ( (uchar) atoi( param[ 0 ] ) ) & 0x3F;
switch ( cmd ) {
case _SET : _SR1F = read_SR( 0x1F) & 0xC0; // XX00 0000
// Now perform a READ/MODIFY/WRITE to _SR1F
write_SR( 0x1F, _SR1F | new_MCLK );
msgout << "\nNew MCLK = " << get_mclkfreq() << "MHz ("
<< (int)( new_MCLK ) << "d)";
// sprintf( msg.temp, "\nNew MCLK = %.2fMHz (%02dd) ",
// get_mclkfreq(), new_MCLK );
// strcat( msg.text, msg.temp );
break;
case _GET : case _HELP: msgout
<< "\nNote: GD-5429 max = 60MHz, all other GD=542x = 50MHz"
<< "\n GD-5430/5440 max = 60MHz, GD-5436 = 80MHz"
<< "\n\tInput = 0-63dec, example values : "
<< "\n\t23d = 41.17MHz\n\t25d = 44.74MHz"
<< "\n\t28d = 50.11MHz\n\tOther values ok.";
break;
default:
msgout << "cirrus::_mclk(cmd) UNRECOGNIZED cmd.";
status = EXIT_FAILURE;
}
msgout << ends;
}
void
_GD5424::_fxn2( int cmd ) // RDY delay for I/O (local bus only)
{
INITMSG( msg.text );
if ( cmd == _QUERY ) {
msgout << "2 GD-5424/6/8/9 RDY delay for I/O (local-bus only)\n"
<< ends;
return;
}
uchar _SR16 = ( read_SR( 0x16 ) >> 6 ) & 0x03; // Read _SR16 register
uchar new_RDY= 0x03; // Default to 2T delay
if ( param[ 0 ] != NULL )
new_RDY = (uchar)atoi( param[ 0 ] );
// sprintf( msg.text, "Old I/O RDY delay = %uT (%ud) ", _SR16 / 2, _SR16 );
msgout << "Old I/O RDY delay = " << (int) (_SR16 / 2) << "T ("
<< (int)_SR16 << ") ";
switch ( cmd ) {
case _SET: _SR16 = read_SR( 0x16 ) & 0x3F; // DD00 0000
write_SR( 0x16, ( ( new_RDY << 6 ) & 0xC0 ) | _SR16 );
// sprintf( msg.temp, "\nNew delay = %uT (%ud) ",
// new_RDY /2 , new_RDY );
// strcat ( msg.text, msg.temp );
msgout << "\nNew delay = " << (int) (new_RDY / 2) << "T ("
<< (int)new_RDY << ") ";
break;
case _GET: case _HELP:
msgout << "\n(local-bus only)\nActual clock delays (T) / XXd"
<< " (where XXd is INPUT value in decimal.)"
<< "\n\t0T/00\n\t0T/01\n\t1T/02\n\t1T/03";
break;
default:
msgout << "_GD5424::_fxn2(cmd) UNRECOGNIZED cmd.";
status = EXIT_FAILURE;
}
msgout << ends;
}
/* REMOVED, BECAUSE CIRRUS BIOS RE-PROGRAMS FIFO THRESHOLD FOR EVERY mode!
void
_GD5424::_fxn4( int cmd ) // FIFO refilling threshold (_SR16 bits 3-0)
{
if ( cmd == _QUERY ) {
strcpy(msg.text, "4 GD-5424+ FIFO Demand Threshold\n" );
return;
}
uchar _SR16 = read_SR( 0x16 ) & 0x0F; // 0000 XXXX
uchar new_SR16= 0x0F; // Default to 15 FIFO slots
if ( param[ 0 ] != NULL )
new_SR16 = (uchar)atoi( param[ 0 ] );
sprintf( msg.text, "Old FIFO threshold = %u ", _SR16 );
switch ( cmd ) {
case _SET: _SR16 = read_SR( 0x16 ) & 0xF0; // DD00 0000
write_SR( 0x16, ( new_SR16 & 0x0F ) | _SR16 );
sprintf( msg.temp, "\nNew FIFO = %u ", new_SR16 );
strcat ( msg.text, msg.temp );
break;
case _GET: case _HELP:
sprintf( msg.temp, "\nFIFO threshold (GD-5424 and up) %s%s%s",
"\nTriggering level for CRT to re-fill CRT FIFO (0-15d)",
"\n\t( lower value = more frequent re-fills )",
"\n\tAllowable input: 0-15 (decimal)");
strcat ( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_GD5424::_fxn4(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
} */
void
_GD5424::_fxn3( int cmd ) // RDY Delay for Memory Write
{
INITMSG( msg.text );
if ( cmd == _QUERY ) {
msgout << "3 GD-5424/6/8/9 RDY Delay for mem-write "
<< "(local-bus only)\n" << ends;
return;
}
uchar _SR16 = ( read_SR( 0x16 ) >> 4 ) & 0x03; // Read _SR16 register
uchar new_RDY= 0x03; // Default to 2T delay
uchar _is5429 = ( strcmpi( id.chipset, "GD-5429" ) == 0 );
// _is5429 is a status-variable, 0 = not 5429
if ( param[ 0 ] != NULL )
new_RDY = (uchar)atoi( param[ 0 ] );
msgout << "Old RDY write-mem delay = " << (int)( 2 - _is5429 + _SR16 )
<< "T (" << (int)( _SR16 ) << ") ";
// sprintf( msg.text, "Old RDY write-mem delay = %uT (%ud) ", ( 2 -
// _is5429 + _SR16 ), _SR16 ) ;
switch ( cmd ) {
case _SET: _SR16 = read_SR( 0x16 ) & 0xCF; // 00DD 0000
write_SR( 0x16, ( ( new_RDY << 4 ) & 0x30 ) | _SR16 );
msgout << "\nNew delay = " << (int)( 2 - _is5429 + new_RDY )
<< "T (" << (int)( new_RDY ) << ") ";
// sprintf( msg.temp, "\nNew delay = %uT (%ud) ", 2 - _is5429 +
// new_RDY, new_RDY );
// strcat ( msg.text, msg.temp );
break;
case _GET: case _HELP: msgout << "\n(local-bus only)"
<< " Actual LRDY I/O clock delays (T) / XXd"
<< "\n(where XXd is INPUT value in decimal.)";
if ( _is5429 )
msgout << "\n\t1T/00\n\t2T/01\n\t3T/02\n\t4T/03";
else
msgout << "\n\t2T/00\n\t3T/01\n\t4T/02\n\t5T/03";
break;
default: msgout << "_GD5424::_fxn3(cmd) UNRECOGNIZED cmd.";
status = EXIT_FAILURE;
}
msgout << ends;
}
//-------------------- Cirrus GD-543x functions --------------------------
void
_GD543x::_fxn2( int cmd ) // LRDY delay for VL-bus only
{ // 0600 0000 bit6 of _SR16 = LRDY delay
INITMSG( msg.text );
if ( cmd == _QUERY ) {
msgout << "2 GD-543x RDY delay for I/O (local-bus only)\n"
<< ends;
return;
}
uchar _SR16 = read_bit( _SRindex, 0x16, 6 ); // Read bit6 _SR16 register
uchar new_RDY= 0x01; // Default to 2T READ/ 1T WRITE delay
if ( param[ 0 ] != NULL )
new_RDY = (uchar)atoi( param[ 0 ] ) ;
sprintf( msg.text, "Old vl-bus LRDY R/W delay = %uT/%uT (%ud) ", 1 +
_SR16, _SR16, _SR16 );
switch ( cmd ) {
case _SET:
write_bit( _SRindex, 0x16, 6, new_RDY );
sprintf( msg.temp, "\nNew R/W delay = %uT/%uT (%ud) ", 1 +
new_RDY, new_RDY, new_RDY );
strcat ( msg.text, msg.temp );
break;
case _GET: case _HELP:
sprintf( msg.temp,"\n(only applicable in VL-bus setup.)%s",
"\n\t1T read/0T write - 00\n\t2T read/1T write - 01");
strcat ( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_GD543x::_fxn2(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_GD543x::_fxn3( int cmd ) // LRDY Delay for Memory Cycles
{ // LRDY Delay = bit4 of _SR16
INITMSG( msg.text );
if ( cmd == _QUERY ) {
msgout << "3 GD-543x RDY Delay for mem-write (local-bus only)\n"
<< ends;
return;
}
uchar _SR16 = read_bit( _SRindex, 0x16, 4 ); // Read bit4 _SR16 register
uchar new_RDY= 0x01; // Default to 2T-1T R/W delay
if ( param[ 0 ] != NULL )
new_RDY = (uchar)atoi( param[ 0 ] );
sprintf( msg.text, "Old LRDY R/W mem delay = %uT/%uT (%ud) ", 1 +
_SR16, _SR16, _SR16 ) ;
switch ( cmd ) {
case _SET:
write_bit( _SRindex, 0x16, 4, new_RDY );
sprintf( msg.temp, "\nNew R/W delay = %uT/%uT (%ud) ", 1 +
new_RDY, new_RDY, new_RDY ) ;
strcat ( msg.text, msg.temp );
break;
case _GET: case _HELP:
sprintf( msg.temp, "\n(local-bus only)%s",
"\nRead delay / Write delay\n\t1T/0T - 00\n\t2T/1T - 01" );
strcat( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_GD543x::_fxn3(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_GD5434::_mclk( int cmd )
{
uchar _SR1F = read_SR ( 0x1F ) & 0x3F; // Read _SR1F register
uchar new_MCLK = _SR1F; // New MCLK byte
union REGS reg;
INITMSG( msg.text );
if ( cmd == _QUERY ) {
msgout << "0 GD-5434 MCLK memory clock setting\n" << ends;
return;
}
// sprintf( msg.text, "Current MCLK = %.2fMHz (%02dd)", get_mclkfreq(),
// _SR1F );
msgout.precision( 2 );
msgout << "Old MCLK = " << get_mclkfreq() << "MHz (" << (int)(_SR1F)
<< "d)";
if ( param[ 0 ] != NULL ) // Only the 1st parameter is used
new_MCLK = ( (uchar) atoi( param[ 0 ] ) ) & 0x3F;
switch ( cmd ) {
case _SET : _SR1F = read_SR( 0x1F) & 0xC0; // XX00 0000
// Now perform a READ/MODIFY/WRITE to _SR1F
write_SR( 0x1F, _SR1F | new_MCLK );
msgout << "\nNew MCLK = " << get_mclkfreq() << "MHz ("
<< (int)( new_MCLK ) << "d)";
// sprintf( msg.temp, "\nNew MCLK = %.2fMHz (%02dd) ",
// get_mclkfreq(), new_MCLK );
// strcat( msg.text, msg.temp );
break;
case _GET : case _HELP: msgout << "\nGD-5434 max = 50MHz, "
<< "GD-5434revE max = 60MHz\nInput = 0-63dec, example values "
<< ": \n\t23d = 41.17MHz\n\t25d = 44.74MHz"
<< "\n\t28d = 50.11MHz\n\tOther values ok.\n\t**WARNING** "
<< "GD-5434's video BIOS resets MCLK on every mode change!";
// strcat( msg.text, msg.temp );
break;
default:
msgout << "_GD5434::_mclk(cmd) UNRECOGNIZED cmd.";
status = EXIT_FAILURE;
}
msgout << ends;
}
void
_GD5436::_fxn1( int cmd ) // Enable 8-MCLK EDO Timing
{ // bit2 of _GR18, 0 = no, 1 = 8-MCLK EDO timing
INITMSG( msg.text );
if ( cmd == _QUERY ) {
msgout << "1 GD-5436/46 8-MCLK EDO DRAM timing\n" << ends;
return;
}
uchar new_EDO = read_bit( _GRindex, 0x18, 2 ); // Default to old val
sprintf( msg.text, "Old 8-MCLK EDO timing = %s", bitstat( new_EDO ) );
if ( param[ 0 ] != NULL )
new_EDO = (uchar)atoi( param[ 0 ] );
switch ( cmd ) {
case _SET: write_bit( _GRindex, 0x18, 2, new_EDO );
sprintf( msg.temp, "\n8-MCLK EDO timing is now %s ",
bitstat( new_EDO ) );
strcat ( msg.text, msg.temp );
break;
case _GET: case _HELP:
sprintf( msg.temp, "\n8-MCLK EDO Timing\n\t1 = enable%s%s",
"\n\t0 = disable\nDisable for FPM-DRAM, activate for EDO-DRAM timing",
"\n(Increased delay may permit higher MCLK-frequencies" );
strcat ( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_GD5436::_fxn1(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
void
_GD5436::_fxn2( int cmd ) // Single Refresh Cycle
{ // bit3 of _GR18, 0 = standard timing, 1 = single-refresh cycle
INITMSG( msg.text );
if ( cmd == _QUERY ) {
msgout << "2 GD-5436/46 RAM refresh-cycle timing\n" << ends;
return;
}
uchar new_CYC = read_bit( _GRindex, 0x18, 3 ); // Default to old val
sprintf( msg.text, "Single-refresh cycle timing = %s",
bitstat( new_CYC ) );
if ( param[ 0 ] != NULL )
new_CYC = (uchar)atoi( param[ 0 ] );
switch ( cmd ) {
case _SET: write_bit( _GRindex, 0x18, 3, new_CYC );
sprintf( msg.temp, "\nSingle-cycle refresh timing is now %s.",
bitstat( new_CYC ) );
strcat ( msg.text, msg.temp );
break;
case _GET: case _HELP:
sprintf( msg.temp, "\nSingle-cycle refresh\n\t1 = enable%s%s",
"\n\t0 = disable\n\nEnabling single-cycle refresh ",
"increases available memory-bandwidth." );
strcat ( msg.text, msg.temp );
break;
default:
sprintf( msg.text, "_GD5436::_fxn2(cmd) UNRECOGNIZED cmd.");
status = EXIT_FAILURE;
}
}
_GD5462::_GD5462( vga_info info ) : vga( info )
{
// First, get and store original MMIO0 base-address
// We need to keep it, because it'll be changed later
baseio.b.b0 = read_cbyte( 0x10 +0 ); // Read PCICFG$10, bits 7:0
baseio.b.b1 = read_cbyte( 0x10 +1 ); // Read PCICFG$11, bits 15:8
baseio.b.b2 = read_cbyte( 0x10 +2 ); // Read PCICFG$12, bits 23:16
baseio.b.b3 = read_cbyte( 0x10 +3 ); // Read PCICFG$13, bits 31:24
// Byte3 = most-significant byte, Byte0= least-significant)
// Actually, the Cirrus Logic mmio0 spans bits 31:15 at PCICFG$10
// We don't need to store b0. We only need to store bit7 of b1
// MMIO aperature = 32KB (mmio0 address = 32kB granularity)
// For aperature at A000:0000 (absolute offset 6556360 decimal)
// Must set MMIO[31:15] = $14 (hex)
// --> translates into b3 = 0x0A, b2 = 0, b1[bit 7] = 0
// Now we have to enable I/O access to MMIO registers
pci_command.b.b0 = read_cbyte( 0x04 ); // Preserve original value
pci_command.b.b1 = read_cbyte( 0x05 ); // Preserve original value
write_cbyte( 0x04, pci_command.b.b0 | 0x01 ); // Write out XXXX XXX1
framebuffer = (uchar*)MK_FP( 0xA000, 0x0000 );
// Set framebuffer to point to VGA framebuffer address 0xA0000
}
_GD5462::~_GD5462()
{
// Restore original MMIO aperature location
write_cbyte( 0x10, baseio.b.b0 );
write_cbyte( 0x11, baseio.b.b1 );
write_cbyte( 0x12, baseio.b.b2 );
write_cbyte( 0x13, baseio.b.b3 );
// Now restore pci_command register
write_cbyte( 0x04, pci_command.b.b0 );
write_cbyte( 0x05, pci_command.b.b1 );
}
message
_GD5462::_info( void )
{
INITMSG( msg.text );
msgout << "IO config address = 0x" ; // 32-bit absolute byte address
hexout( msgout, baseio.b.b3 ); // print reg.h.bl in "XX" format
hexout( msgout, baseio.b.b2 );
msgout << " ";
if ( baseio.b.b1 & 0x80 ) // Is bet7 of b1 set?
msgout << "8000"; // Yup! +32Kbyte offset
else
msgout << "0000"; // Nope! No such offset
msgout << ends ;
return msg;
}
uchar
_GD5462::read_cbyte( const uchar index )
{
uchar value, status = TRUE;
status = pci_bios->read_cbyte( pci_vga, index, &value );
return value;
}
uchar
_GD5462::write_cbyte( const uchar index, const uchar value )
{
uchar status= TRUE;
if ( pci_bios->write_cbyte( pci_vga, index, value ) != 0 )
status = FALSE;
return status;
}
uchar
_GD5462::get_mclkbyte( void )
{
uchar mclk_byte;
// Set MMIO0 aperature to A0000, so it's readable by real-mode apps
// Actually, the Cirrus Logic mmio0 spans bits 31:15 at PCICFG$10
// We don't need to store b0. We only need to store bit7 of b1
// MMIO aperature = 32KB (mmio0 address = 32kB granularity)
// For aperature at A000:0000 (absolute offset 6556360 decimal)
// Must set MMIO[31:15] = $14 (hex)
// --> translates into b3 = 0x0A, b2 = 0, b1[bit 7] = 0
write_cbyte( 0x12, 0x0A ); // base-address[8:1] = 0x0A
write_cbyte( 0x13, 0x00 ); // base-address[16:9] = 0x00
write_cbyte( 0x11, read_cbyte( 0x11 ) & 0x7F );
// bit7=0, or base-address[0] = 0
mclk_byte = *( framebuffer + 0x8C ); // read MMIO0 offset 0x8C
// Restore MMIO0 aperature to original value
write_cbyte( 0x11, baseio.b.b1 );
write_cbyte( 0x12, baseio.b.b2 );
write_cbyte( 0x13, baseio.b.b3 );
return mclk_byte;
}
void
_GD5462::_mclk( int cmd )
{
union {
uchar byte; // MCLK register byte
struct {
unsigned mult : 5; // RAMBUS multiplier, 5-bits
unsigned other : 3;
} x;
} mclk;
INITMSG( msg.text );
if ( cmd == _QUERY ) {
msgout << "0 GD-546X RAMBUS clock setting\n" << ends;
return;
}
mclk.byte = get_mclkbyte(); // Get mclk_byte
msgout.precision( 2 );
msgout << "Old RAMBUS clock = " << ( _OSC * mclk.x.mult ) << " MHz ("
<< (int)( mclk.x.mult ) << "d)";
if ( param[ 0 ] != NULL ) // Only the 1st parameter is used
mclk.x.mult = (unsigned) atoi( param[ 0 ] ) & 0x1F;
switch ( cmd ) {
case _SET : // First, set MMIO address to A000:0000
write_cbyte( 0x12, 0x0A ); // base-address[15:0] = 0x000A
write_cbyte( 0x13, 0x00 );
write_cbyte( 0x11, read_cbyte( 0x11 ) & 0x7F );
// bit7=0, or base-address[0] = 0
*( framebuffer + 0x8C ) = mclk.byte;
// Write mclk_byte -> MMIO0 offset 0x8C
// Restore MMIO0 aperature to original value
write_cbyte( 0x11, baseio.b.b1 );
write_cbyte( 0x12, baseio.b.b2 );
write_cbyte( 0x13, baseio.b.b3 );
mclk.byte = get_mclkbyte(); // Re-read mclk_byte
msgout << "\nNew RAMBUS clock = " << ( _OSC * mclk.x.mult ) <<
" MHz (" << (int)( mclk.x.mult ) << "d)";
break;
case _GET : case _HELP: msgout
<< "\nNote: GD-546X maximum RAMBUS clock (BCLK) = "
<< "258MHz (18d),\n\tInput = 7-22dec, example values :"
<< "\n\t18 = 257.73MHz\n\t20 = 286.36MHz\n\tOther values ok."
<< "\n\nWARNING! Win95 drivers reset RAMBUS clock! See "
<< "546X.TXT for details!";
break;
default:
msgout << "_GD5462::_mclk(cmd) UNRECOGNIZED cmd.";
status = EXIT_FAILURE;
}
msgout << ends;
}
void
_GD5464::_fxn2( int cmd ) // PCI Master Latency Timer Register
{ // PCI Config $0D[7:3]
union {
uchar byte; // PCI Master latency timer register byte
struct {
unsigned other : 3;
unsigned timer : 5; // PCI latency timer, 5-bits
} x;
} pcfg;
INITMSG( msg.text );
if ( cmd == _QUERY ) {
msgout << "2 GD-5464 PCI Master Latency Timer\n" << ends;
return;
}
pcfg.byte = read_cbyte( 0x0D); // Get pcfg_byte
msgout << "Old Latency Timer=" << (int)( pcfg.x.timer ) << " cycle(s)";
if ( param[ 0 ] != NULL ) // Only the 1st parameter is used
pcfg.x.timer = (unsigned) atoi( param[ 0 ] ) & 0x1F;
switch ( cmd ) {
case _SET : write_cbyte( 0x0D, pcfg.byte );
pcfg.byte = read_cbyte( 0x0D ); // Re-read pcfg_byte
msgout << "\nNew Latency Timer=" << (int)( pcfg.x.timer ) <<
" cycle(s)";
break;
case _GET : case _HELP: msgout
<< "\nGD-5464 PCI Latency Timer\n Controls latency when "
<< "GD-5464 acts as a PCI-bus master.\n\tInput = 0-31 "
<< "(number of PCI clock cycles)";
break;
default:
msgout << "_GD5464::_fxn2(cmd) UNRECOGNIZED cmd.";
status = EXIT_FAILURE;
}
msgout << ends;
}