/****************************************************************************
 * CC.C                                                                     *
 *                                                                          *
 * Purpose: Sample program to demonstrate closed captioning support with    *
 *          the Rage Theatre chip.                                          *
 *                                                                          *
 * Copyright (C) 1999 ATI Technologies Inc.  All rights reserved.           *
 ****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <ctype.h>
#include <string.h>
#include "..\util\main.h"
#include "..\util\defines.h"
#include "..\util\regdef.h"

DWORD SRCWIDTH = 640;
DWORD SRCHEIGHT = 240; // we're only capturing one field at a time.
DWORD OVWIDTH = 640;
DWORD OVHEIGHT = 480;
WORD CHANNEL = 33;
BYTE DEFAULT_VOLUME = 0x67;

DWORD VBICaptureSize;

void R128_EnableVBICapture (CAPTURE_DESCRIPTOR *cap);
void R128_CheckVBIInt (CAPTURE_DESCRIPTOR *cap);
void R128_ClearVBIInt (void);
WORD RT_DecodeCCData (BYTE *Data);
void ProcessVBIData (BYTE *buffer);
BYTE InverseByte (BYTE input);

/****************************************************************************
 * Main Program to demonstrate Rage Theatre video decoder                   *
 *  Function: To display video using the Rage Theatre as the video decoder  *
 *    Inputs: Arguments for mode spatial and colour resolution              *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void main (int argc, char *argv[])
{
    CAPTURE_DESCRIPTOR cap;
    DWORD x, y;
    char buffer[80];
    BYTE volume = DEFAULT_VOLUME;

    RT_INFO = &RT_DATA;

    R128_StartUp (argc, argv);
    R128_ClearScreen (WHITE);
    R128_LoadText ();
    R128_GetPLLInfo ();

    // set up the I2C engine
    R128_SetI2CInfo (10000);  // use a 10 kHz clock

    // enable I2C hardware
    I2C_Enable ();

    AddressInfo.Tuner = 0xC6;

    // Initialize the VIP subsystem
    R128_InitVIP ();

    // Find the Rage Theatre's VIP device number
    RT_DEVICE_NUM = RT_GetVIPAddress ();

    if (RT_DEVICE_NUM < 0)
    {
        // Shut down and exit, we cannot find a Rage Theatre.
        I2C_Stop ();
        I2C_Disable ();
        R128_ShutDown ();
        printf ("\nRage Theatre not detected.\nProgram terminated.");
        exit (1);
    }

    // Read in the multimedia table from the BIOS, for connector information
    R128_GetMMTable (&MMTABLE);

    // Initialize and enable the R128 capture 0 unit
    R128_InitCaptureDescriptor (&cap);
    // set up the capture buffers and other fields we want to use
    cap.buf0_offset = R128_AdapterInfo.xres * R128_AdapterInfo.bytepp * R128_AdapterInfo.yres;
    cap.buf0_even_offset = cap.buf0_offset + SRCWIDTH * 2 * SRCHEIGHT;
    cap.input_mode = CAP_MODE_CONTINUOUS;
    cap.buf_mode = CAP_BUF_MODE_SINGLE;
    cap.buf_type = CAP_BUF_TYPE_ALTERNATING;
    cap.video_in_format = CAP_VIDEO_IN_FORMAT_VYUY422;
    cap.stream_format = CAP_STREAM_FORMAT_CCIR656;
    cap.buf_pitch = SRCWIDTH * 2;
    cap.width = SRCWIDTH;
    cap.height = SRCHEIGHT;

    // Initialize and enable the CAP0 capture unit.
    R128_InitCapture (&cap);
    R128_EnableCapture ();

    // Set up the overlay.
    R128_InitOverlayDescriptor (&OverlayDescriptor);
    R128_GetBandwidthInfo (&OverlayDescriptor);
    R128_SetOverlayDefaults (0xB, SRCWIDTH, SRCHEIGHT, 1.0, 1.0, &OverlayDescriptor);
    R128_SetupOverlay (&OverlayDescriptor, &OverlayRegFields);
    R128_SetOverlayDetailedSurface ();
//    R128_SetOverlayColourKey (LIGHTMAGENTA, WHITE, 5, 0, 0);

    // Set the x and y location of the overlay display
    x = R128_AdapterInfo.xres - OVWIDTH;
    y = R128_AdapterInfo.yres - OVHEIGHT;

    // Draw the keying rectangle.
    R128_DrawRectangle (x, y, OVWIDTH, OVHEIGHT, LIGHTMAGENTA);
    sprintf (buffer, "Channel: %d", CHANNEL);
    R128_PrintText (buffer, x+5, y+5, LIGHTGREEN, WHITE, 1);
    R128_SetOverlay (x, y, OVWIDTH, OVHEIGHT);

    // Enable Rage Theatre.
    RT_Init ();
    AddressInfo.Tuner = 0xC6;
    R128_SetTunerChannel (CHANNEL);

    // Enable audio.
    EnableMSP3430 ();
    MSP3430_SetVolume (volume);

    // Set up the Rage 128 for VBI Capture
    R128_EnableVBICapture (&cap);

    while (!kbhit ())
    {
        // Draw the keying rectangle.
        //R128_DrawRectangle (x, y, OVWIDTH, OVHEIGHT, LIGHTMAGENTA);
        //if (RT_INFO->wConnector == DEC_TUNER)
        //{
            //sprintf (buffer, "Channel: %d", CHANNEL);
            //R128_PrintText (buffer, x+5, y+5, LIGHTGREEN, WHITE, 1);
        //}
        //R128_SetOverlay (x, y, OVWIDTH, OVHEIGHT);

        R128_CheckVBIInt (&cap);

    } // while

    // Shutting down
    DisableMSP3430 ();
    RT_ShutDown ();
    R128_DisableCapture ();

    I2C_Stop ();
    I2C_Disable ();

    R128_DisableOverlay ();
    R128_Delay (5);

    R128_ShutDown ();

    exit (0);

} // main ()...

/****************************************************************************
 * R128_EnableVBICapture (CAPTURE_DESCRIPTOR *cap)                          *
 *  Function: sets up and enables VBI capture for the Rage 128              *
 *    Inputs: CAPTURE_DESCRIPTOR *cap                                       *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void R128_EnableVBICapture (CAPTURE_DESCRIPTOR *cap)
{
    DWORD data;

    RT_regw (VIP_VBI_SCALER_CONTROL, 0x10000);

    R128_WaitForIdle ();

    // Set up for VBI capture of line 19 (which is actually 21
    // from the Rage Theatre)
    regw (CAP0_VBI_V_WINDOW, ((0x13) << 16) | 0x13);
    //regw (CAP0_VBI_H_WINDOW, (0x640 << 16) | 0x0000);
    regw (CAP0_VBI_H_WINDOW, (0x618 << 16) | 0x0000);

    // Set the VBI capture buffers to the onscreen viewable area
    VBICaptureSize = R128_AdapterInfo.xres * R128_AdapterInfo.bytepp * 20;
    cap->vbi_odd_offset = VBICaptureSize;
    cap->vbi_even_offset = VBICaptureSize * 2;

    regw (CAP0_VBI_ODD_OFFSET, cap->vbi_odd_offset);
    regw (CAP0_VBI_EVEN_OFFSET, cap->vbi_even_offset);
    regw (CAP0_CONFIG, regr (CAP0_CONFIG) | 0x2000);

    // Enable the VBI capture interrupt
    data = regr (CAP_INT_CNTL);
    data |= 0x10; // set bit 4 = CC data is only in the odd field
    regw (CAP_INT_CNTL, data);

    // Clear the interrupt by writing 1 to the appropriate bit
    data = regr (CAP_INT_STATUS);
    data &= 0x10;
    regw (CAP_INT_STATUS, data);

} // R128_EnableVBICapture ()...


/****************************************************************************
 * R128_ClearVBIInt (void)                                                  *
 *  Function: Clears the VBI Capture Interrupt bits                         *
 *    Inputs: NONE                                                          *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void R128_ClearVBIInt (void)
{
    DWORD temp;

    R128_WaitForFifo (2);
    temp = regr (CAP_INT_STATUS);

    // clear the VBI0 interrupt bit
    temp &= 0x10;
    regw (CAP_INT_STATUS, temp);
    return;

} // R128_ClearVBIInt ()...

/****************************************************************************
 * R128_CheckVBIInt (void)                                                  *
 *  Function: Checks the status of the VBI Capture Interrupt bits           *
 *    Inputs: CAPTURE_DESCRIPTOR *cap                                       *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void R128_CheckVBIInt (CAPTURE_DESCRIPTOR *cap)
{
    DWORD temp;

    temp = regr (CAP_INT_STATUS);

    if (temp & 0x10)
    {
        // VBI 0 interrupt has occurred.
        ProcessVBIData ((BYTE *)(R128_AdapterInfo.virtual_MEM_BASE + cap->vbi_odd_offset));
        R128_ClearVBIInt ();
    }

} // R128_CheckVBIInt ()...


/****************************************************************************
 * ProcessVBIData (BYTE *buffer)                                            *
 *  Function: processes the captured VBI data, including parsing            *
 *    Inputs: BYTE *buffer - pointer to the captured VBI data               *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void ProcessVBIData (BYTE *buffer)
{
    BYTE *data = NULL;
    WORD cc_data;
    BYTE ch1, ch2;
    BYTE op[30];

    data = malloc (1560);
    if (data == NULL)
    {
        return;
    }

    memcpy (data, buffer, 1560);

    cc_data = RT_DecodeCCData (data);

    ch1 = InverseByte ((BYTE)(cc_data & 0x00FF));
    ch2 = InverseByte ((BYTE)((cc_data >> 8) & 0xFF));

    // Print the data (as hex bytes) on the screen
    sprintf (op, "CCData: %4.4X", (ch1 << 8) | ch2);
    R128_DrawRectangle (20, 230, (TEXT_INFO.actual_width*12), 40, WHITE);
    R128_PrintText (op, 20, 230, BLACK, WHITE, 1);

    free (data);

} // ProcessVBIData ()...

/****************************************************************************
 * BYTE InverseByte (BYTE input)                                            *
 *  Function: inverts the input byte                                        *
 *    Inputs: BYTE input - byte that must be inverted                       *
 *   Outputs: BYTE - the inverted byte                                      *
 ****************************************************************************/
BYTE InverseByte (BYTE input)
{
    BYTE output = 0;
    BYTE x, y;

    for (x = 0x01, y = 0x80; y; x<<=1, y>>=1)
    {
        if (input & x)
        {
            output |= y;
        }
    }
    return (output);

} // InverseByte ()...

