#include "stdafx.h"
#include "AD9824.h"
#include "Timer.h"
#include "Trace.h"

// Declare and initialise static data members

// Bit offset for each register
const int IoAAD9824Register::m_OffsetLUT[ nAD9824RegisterCount ] = { 0, 11, 21, 29, 39, 45, 51, 57 };

// Number of bits in each register
const int IoAAD9824Register::m_CountLUT[ nAD9824RegisterCount ] = { 11, 10, 8, 10, 6, 6, 6, 6 };

// String to identify each register
const char *IoAAD9824Register::m_NameLUT[ nAD9824RegisterCount ] =
{
    "Operation", "VGAGain", "ClampLevel", "Control", "PxGAGain0", "PxGAGain1", "PxGAGain2", "PxGAGain3"
};

// Variable offset for each register
const int IoAAD9824Register::m_VariableOffsetLUT[ nAD9824RegisterCount ] =
{
    eChannelSelect, eVGAGain, eClampLevel, eColourSteeringMode, ePxGAGain0, ePxGAGain1, ePxGAGain2, ePxGAGain3,
};

// Number of variables in each register
const int IoAAD9824Register::m_VariableCountLUT[ nAD9824RegisterCount ] =
{
    eOpticalBlackClampDisable - eChannelSelect      + 1,
    eVGAGain                  - eVGAGain            + 1,
    eClampLevel               - eClampLevel         + 1,
    eDataOutDisable           - eColourSteeringMode + 1,
    ePxGAGain0                - ePxGAGain0          + 1,
    ePxGAGain1                - ePxGAGain1          + 1,
    ePxGAGain2                - ePxGAGain2          + 1,
    ePxGAGain3                - ePxGAGain3          + 1
};

// Bit offset for each register variable
const int IoAAD9824Variable::m_OffsetLUT[ nAD9824VariableCount ] =
{
    0, 2, 4, 5, 11, 21, 29, 32, 33, 34, 35, 38, 39, 45, 51, 57
};

// Numbers of bits in each register variable
const int IoAAD9824Variable::m_CountLUT[ nAD9824VariableCount ] =
{
    2, 2, 1, 1, 10,  8,  3,  1,  1,  1,  1,  1,  6,  6,  6,  6
};

// String to identify each register variable
const char *IoAAD9824Variable::m_NameLUT[ nAD9824VariableCount ] =
{
    // Operation register
    "ChannelSelect", "PowerDownMode", "RegisterReset", "OpticalBlackClampDisable",

    // VGA Gain register
    "VGAGain", 

    // Clamp Level register
    "ClampLevel", 

    // Control register
    "ColourSteeringMode", "PxGAEnable", "SHPAndSHDActiveHigh", "CLPAndPBLKActiveHigh", "TriggerOnDataClockFallingEdge",
    "DataOutDisable",

    // PxGAGain0 register
    "PxGAGain0",

    // PxGAGain0 register
    "PxGAGain1",

    // PxGAGain2 register
    "PxGAGain2",

    // PxGAGain3 register
    "PxGAGain3"
};

void IoAAD9824Register::Set( int Value )
{
    // Enforce mandatory bit settings in Operation and Control registers
    if ( IsOperationRegister( ) )
    {
        Value &= ~0x740;  // bits 6, 8, 9 and 10 = 0
        Value |=  0x080;  // bit 7 = 1
    }
    else if ( IsControlRegister( ) )
    {
        Value &= ~0x180;  // bits 7 and 8 = 0
    }

    // Store new register bit settings
    IoAAD9824Data::SetValue( m_Offset, m_Count, Value );
}

void IoAAD9824Registers::SetDefaults( )
{  /*
    // Operation register = 0x000
    SetChannelSelect( eCCDMode );
    SetPowerDownMode( eNormalPower );
    DisableRegisterReset( );
    DisableOpticalBlackClamp( );

    // VGA gain register = 0x000
    SetVGAGain( 0.0 );

    // Clamp level register = 0x080
    SetClampLevel( 512 );

    // Control register = 0x000
    SetColourSteeringMode( eSteeringDisabled );
    DisablePxGA( );
    SetSHPAndSHDActiveLow( );
    SetCLPAndPBLKActiveLow( );
    SetTriggerOnDataClockRisingEdge( );
    EnableDataOut( );

    // PxGAGain registers = 0x000
    SetPxGAGain( 0, 3.6 );
    SetPxGAGain( 1, 3.6 );
    SetPxGAGain( 2, 3.6 );
    SetPxGAGain( 3, 3.6 );

    // Mandatory bit settings
    SetMandatoryBits( );
*/
    // Set default register contents directly
    ( *this )[ eOperationRegister  ] = 0x000;
    ( *this )[ eVGAGainRegister    ] = 0x000;
    ( *this )[ eClampLevelRegister ] = 0x080;
    ( *this )[ eControlRegister    ] = 0x000;  // mandatory bit settings enforced via IoAAD9824Register::Set( ) function
    ( *this )[ ePxGAGainRegister0  ] = 0x000;
    ( *this )[ ePxGAGainRegister1  ] = 0x000;
    ( *this )[ ePxGAGainRegister2  ] = 0x000;
    ( *this )[ ePxGAGainRegister3  ] = 0x000;
}

void IoAAD9824Registers::SetMandatoryBits( )
{
    // Enforce mandatory bit settings in Operation and Control registers
    ( *this )[  6 ] = 0;
    ( *this )[  8 ] = 0;  // bits 6, 8, 9, 10 36 and 37 = 0
    ( *this )[  9 ] = 0;
    ( *this )[ 10 ] = 0;
    ( *this )[ 36 ] = 0;
    ( *this )[ 37 ] = 0;

    ( *this )[  7 ] = 1;  // bit 7 = 1
}

int IoAAD9824::Open( int /* Verify */ )
{
    Close( );

    // Synchronize software and hardware registers
    if ( ! Write( m_Registers ) ) return FALSE;
/*
    // Verify synchronization process by reading back and comparing register contents and comparing
    if ( Verify )
    {
        IoAAD9824Registers Registers;

        // Read hardware register contents
        if ( ! Read( Registers ) ) return FALSE;

        // Verify (compare) register contents
        if ( Registers != m_Registers ) return FALSE;
    }
*/
    IoADevice::Open( );  // flag device as open

    return TRUE;
}

int IoAAD9824::Synchronize( const IoAAD9824Register& Register )
{
    if ( Register != m_Registers[ Register.Enum( ) ] )
    {
        if ( IsOpen( ) )
        {
            // See if Register Reset is enabled (must get reset out of the way first)
            if ( Register.IsRegisterResetEnabled( ) )
            {
                // Carry out Register Reset (clears Register Reset bit)
                if ( ! RegisterReset( ) ) return FALSE;
            }

            if ( ! Write( Register ) ) return FALSE;
        }

        m_Registers[ Register.Enum( ) ] = Register;

        IoATrace( eTraceAD9824Diagnostic, "IoAAD9824::Synchronize( ): successfully updated register => %s\n", Register.Name( ) );

        for ( int v = 0; v < Register.VariableCount( ); v++ )
        {
            IoAAD9824VariableIndex Index = (IoAAD9824VariableIndex) v;

            IoATrace( eTraceAD9824Diagnostic, "IoAAD9824::Synchronize( ): %s = %d\n", Register[ Index ].Name( ),
                                                                                      Register[ Index ].Value( ) );
        }
    }

    return TRUE;
}

int IoAAD9824::Synchronize( const IoAAD9824Registers& Registers )
{
    if ( Registers != m_Registers )
    {
        if ( IsOpen( ) )
        {
            // See if Register Reset is enabled (must get reset out of the way first)
            if ( Registers.IsRegisterResetEnabled( ) )
            {
                // Carry out Register Reset (clears Register Reset bit)
                if ( ! RegisterReset( ) ) return FALSE;
            }

            if ( ! Write( Registers ) ) return FALSE;
        }

        m_Registers = Registers;

        for ( int r = 0; r < Registers.RegisterCount( ); r++ )
        {
            IoAAD9824RegisterEnum Register = (IoAAD9824RegisterEnum) r;

            IoATrace( eTraceAD9824Diagnostic, "IoAAD9824::Synchronize( ): successfully updated register => %s\n",
                                              Registers[ Register ].Name( ) );

            for ( int v = 0; v < Registers[ Register ].VariableCount( ); v++ )
            {
                IoAAD9824VariableIndex Index = (IoAAD9824VariableIndex) v;

                IoATrace( eTraceAD9824Diagnostic, "IoAAD9824::Synchronize( ): %s = %d\n",
                                                  Registers[ Register ][ Index ].Name( ),
                                                  Registers[ Register ][ Index ].Value( ) );
            }
        }
    }

    return TRUE;
}
/*
int IoAAD9824::Verify( const IoAAD9824Register& Register )
{
    if ( ! IsOpen( ) ) return FALSE;

    // Read register data
    if ( ! Read( m_Registers[ Register.Enum( ) ] ) ) return FALSE;

    // Verify contents of register
    if ( Register == m_Registers[ Register.Enum( ) ] )
    {
        IoATrace( eTraceAD9824Diagnostic, "IoAAD9824::Verify( ): successfully verified register => %s\n", Register.Name( ) );

        return TRUE;
    }
    else
    {
        IoATrace( eTraceAD9824Error, "IoAAD9824::Verify( ): failed to verify register => %s\n", Register.Name( ) );

        for ( int v = 0; v < Register.VariableCount( ); v++ )
        {
            IoAAD9824VariableIndex Index = (IoAAD9824VariableIndex) v;

            IoATrace( eTraceAD9824Error, "IoAAD9824::Verify( ): %s = %d != %d\n",
                                         Register[ Index ].Name( ),
                                         Register[ Index ].Value( ),
                                         m_Registers[ Register.Enum( ) ][ Index ].Value( ) );
        }

        return FALSE;
    }
}

int IoAAD9824::Verify( const IoAAD9824Registers& Registers )
{
    if ( ! IsOpen( ) ) return FALSE;

    // Read register data
    if ( ! Read( m_Registers ) ) return FALSE;  // remains to be seen whether all registers support read access

    // Verify contents of all registers
    if ( Registers == m_Registers )
    {
        IoATrace( eTraceAD9824Diagnostic, "IoAAD9824::Verify( ): successfully verified all registers\n" );

        return TRUE;
    }
    else
    {
        for ( int r = 0; r < nAD9824RegisterCount; r++ )
        {
            IoAAD9824RegisterEnum Register = (IoAAD9824RegisterEnum) r;

            if ( Registers[ Register ] != m_Registers[ Register ] )
            {
                IoATrace( eTraceAD9824Error, "IoAAD9824::Verify( ): failed to verify register => %s\n",
                                             Registers[ Register ].Name( ) );

                for ( int v = 0; v < Registers[ Register ].VariableCount( ); v++ )
                {
                    IoAAD9824VariableIndex Index = (IoAAD9824VariableIndex) v;

                    IoATrace( eTraceAD9824Diagnostic, "IoAAD9824::Verify( ): %s = %d != %d\n",
                                                      Registers[ Register ][ Index ].Name( ),
                                                      Registers[ Register ][ Index ].Value( ),
                                                      m_Registers[ Register ][ Index ].Value( ) );
                }
            }
        }

        return FALSE;
    }
}
*/
int IoAAD9824::RegisterReset( )
{
    // Set Register Reset bit
    m_Registers[ eOperationRegister ].EnableRegisterReset( );

    // Update Operation register
    if ( ! Write( m_Registers[ eOperationRegister ] ) ) return FALSE;

    // Pause for a short while to allow reset to take effect
    IoATimer Timer;

    Timer.Wait( 5 );  // 5 millisecond wait
/*
    // Re-synchronize register contents
    if ( ! Read( m_Registers ) ) return FALSE;

    // Make sure Register Reset bit is turned off (it appears that the AD9824 does not do this automatically)
    if ( m_Registers[ eOperationRegister ].IsRegisterResetEnabled( ) )
    {
        m_Registers[ eOperationRegister ].DisableRegisterReset( );

        if ( ! Write( m_Registers[ eOperationRegister ] ) ) return FALSE;
    }
*/
    // Set register contents to default values (default for reset bit is off)
    m_Registers.SetDefaults( );

    // Re-synchronize Operation register contents (it appears that the AD9824 does not turn off the reset bit after a reset)
    if ( ! Write( m_Registers[ eOperationRegister ] ) ) return FALSE;

    return TRUE;
}
/*
int IoAAD9824::Read( IoAAD9824Register& Register ) const
{
    int Bits[ 11 ];

    // Assemble header bits
    Bits[ 0 ] = 1;  // RNW = 1, read operation

    for ( int i = 0; i < 3; i++ ) Bits[ i + 1 ] = ( Register.Number( ) >> i ) & 0x1;  // register address bits = rrr

    Bits[ 4 ] = 0;  // TESTBIT = 0

    // Write header bits
    if ( ! WriteBSP( 0, Bits, 5, 1 ) ) return FALSE;

    // Read data bits
    if ( ! ReadBSP( 1, Bits, Register.BitCount( ), 0 ) ) return FALSE;

    // Copy register data bits
    for ( int j = 0; j < Register.BitCount( ); j++ ) Register[ j ] = Bits[ j ];

    return TRUE;
}
*/
int IoAAD9824::Write( const IoAAD9824Register& Register ) const
{
    int Bits[ 5 + 11 ];

    // Assemble header bits
    Bits[ 0 ] = 0;  // RNW = 0, write operation

    for ( int i = 0; i < 3; i++ ) Bits[ i + 1 ] = ( Register.Number( ) >> i ) & 0x1;  // register address bits = rrr

    Bits[ 4 ] = 0;  // TESTBIT = 0

    // Assemble data bits
    for ( int j = 0; j < Register.BitCount( ); j++ ) Bits[ j + 5 ] = Register[ j ];

    // Write header + data bits
    if ( ! WriteBSP( 0, Bits, 5 + Register.BitCount( ), 0 ) ) return FALSE;

    return TRUE;
}
/*
int IoAAD9824::Read( IoAAD9824Registers& Registers ) const
{
    int Bits[ 5 + 63 ];

    // Assemble header bits
    Bits[ 0 ] = 1;  // RNW = 1, read operation

    Bits[ 1 ] = 0;
    Bits[ 2 ] = 0;  // register address bits = 000
    Bits[ 3 ] = 0;

    Bits[ 4 ] = 0;  // TESTBIT = 0

    // Write header bits
    if ( ! WriteBSP( 0, Bits, 5, 1 ) ) return FALSE;

    // Read data bits
    if ( ! ReadBSP( 1, Bits, Registers.BitCount( ), 0 ) ) return FALSE;

    // Copy register data bits
    for ( int i = 0; i < Registers.BitCount( ); i++ ) Registers[ i ] = Bits[ i ];
//    Registers.SetBits( Bits );

    return TRUE;
}
*/
int IoAAD9824::Write( const IoAAD9824Registers& Registers ) const
{
    int Bits[ 5 + 63 ];

    // Assemble header bits
    Bits[ 0 ] = 0;  // RNW = 0, write operation

    Bits[ 1 ] = 0;
    Bits[ 2 ] = 0;  // register address bits = 000
    Bits[ 3 ] = 0;

    // TESTBIT = 0
    Bits[ 4 ] = 0;  // TESTBIT = 0

    // Assemble data bits
    for ( int i = 0; i < Registers.BitCount( ); i++ ) Bits[ i + 5 ] = Registers[ i ];

    // Write header + data bits
    if ( ! WriteBSP( 0, Bits, 5 + Registers.BitCount( ), 0 ) ) return FALSE;

    return TRUE;
}
