/******************************************************************************
 * mach64 Chapter 8 sample code                                               *
 *                                                                            *
 * switch.c - This program uses the GT engine to perform a blt using          *
 * the front end scaler to colour space convert the blt.  We will take a      *
 * VYUY source image and then blt it through the front end scaler,            *
 * converting it to RGB, then switch between pixel replication or blending    *
 * when displaying.                                                           *
 *                                                                            *
 * Copyright (c) 1994-1998 ATI Technologies Inc.  All rights reserved.        *
 ******************************************************************************/

#include <stdio.h>
#include "..\util\atim64.h"
#include "..\util\defines.h"
#include "..\util\definevt.h"
#include "..\util\atim64vt.h"
#include "..\util\main.h"

// Prototype
void s_blit (int x, int y, int dwidth, int dheight);

// global
unsigned long INVERT = 0;

// defines
#define UP_ARROW 72
#define DOWN_ARROW 80
#define RIGHT_ARROW 77
#define LEFT_ARROW 75


/******************************************************************************
 * Main Program to demonstrate front end scaler CSC usage/pixel rep/blending  *
 *  Function: A simple screen to screen bitblit is performed on a YUV image   *
 *            that is first loaded up onto the screen, then sent through      *
 *            front end scaler, colour space converted to RGB, scaled, then   *
 *            displayed on the screen using either pixel replication or       *
 *            blending.                                                       *
 *    Inputs: Arguments for mode spatial and colour resolution                *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void main (int argc, char *argv[])
{
    int width;                          // Width of drawing area.
    int height;                         // Height of drawing area.
    int x1;                             // x coordinate of point 1.
    int y1;                             // y coordinate of point 1.
    int x2;                             // x coordinate of point 2.
    int y2;                             // y coordinate of point 2.
    int swidth;                         // Width of rectangle or blit.
    int sheight;                        // Height of rectangle or blit.
    int BLEND_OR_REPLICATE = 0;               // Blend or replicate toggle
    unsigned long int ISGT, temp, destbpp;
    int iReturnValue;
    img_handle *ReturnPtr;
    int x, newx, newy, newdheight, newdwidth;
    float scalex, scaley;
    char input = 'T';
    char extcode = '1';
    
    printf ("3D RAGE Chapter 8 sample code\n"
            "\n"
            "switch.c\n"
            "Spatial resolution (640, 800, 1024, 1280, 1600) and Colour Depth\n"
            "(16 or 32) should be passed as arguments.\n"
            "Default setting is 640x480 spatial resolution and 8bpp pixel depth.\n");

    // Batch command to detect the mach64, perform a hardware query, Save old
    // mode information, process mode info arguments, load and set mode, enable
    // aperture, set up palettes, initialize engine to known state, and reset
    // all engine queues.

    // Cases when program fails to set color depth and/or spatial resolution.
    // Program will terminate immediately and returns user to dos prompt.
    if (!(start (argc, argv)))
    {
        printf ("\nPress any key to exit...\n");
        getch ();
        finish ();
        exit (1);
    }

    //  Detect for GT.  Program will terminate if it is not a GT.
    ISGT = is_gt();

    if (ISGT == 0)
    {
        finish ();
        printf ("\nWarning: Unable to detect a 3D RAGE!\n");
        printf ("Reminder: This program only works with a 3D RAGE!\n");
        exit (1);
    }

    // Make sure we are in 16 or 32bpp mode.
    if (MODE_INFO.bpp != 16 && MODE_INFO.bpp != 32)
    {
        // Disable accelerator mode and switch back to VGA text mode.
        finish ();
        printf ("SWITCH requires a colour depth of 16 or 32bpp.\n");
        exit (1);
    } // if


    // Sample drawing routines.
    clear_screen (0, 0, MODE_INFO.xres, MODE_INFO.yres);

    // Load a test image into offscreen memory.
    // This will be the front end scaler source.
    ReturnPtr = get_img_header ("..\\image\\testyvyu.img");
    
    if (ReturnPtr == NULL)
    {
        // Disable accelerator mode and switch back to VGA text mode.

        finish ();
        printf ("Program was not able to read the image header.\n");
        exit (1);
    }

    swidth = (int)ReturnPtr->image->width;

    sheight = (int)ReturnPtr->image->height;
    
    // load source image into offscreen memory. 10 lines beyond the 
    // end of the current display.  I just picked 10 to provide a buffer
    // from the end of displayable memory to the actual image data
    iReturnValue = load_img (ReturnPtr, 0, MODE_INFO.yres + 10);
    if (iReturnValue == LOAD_FAILED)
    {
        // Disable accelerator mode and switch back to VGA text mode.
        finish ();
        printf ("Program was not able to load the image file.\n");
        exit (1);
    }

    // Free up memory used by pointer
    free (ReturnPtr);


    wait_for_fifo (15);

    // Setup front end scaler for the blt
    
    // set up the front end scaler by setting the SCALE_3D_CNTL register
    // this will set up for REPLICATION
    regw (SCALE_3D_CNTL, 0x00000140);

    // setup registers to a known state
    regw (ALPHA_TST_CNTL, 0x00000000);  // ensure that no alpha blending states are on
    regw (TEX_CNTL, 0x00000000); // ensure no lighting states are on
    
    // Set source buffer for front end scaler
    regw (SCALE_OFF, ( (MODE_INFO.yres + 10) * MODE_INFO.xres * 
                                                (MODE_INFO.bpp/8) ) );

    regw (SCALE_PITCH, MODE_INFO.pitch * (MODE_INFO.bpp/16) );

    // Set front end scaler dimensions 
    regw (SCALE_WIDTH, (unsigned long) swidth);
    regw (SCALE_HEIGHT, (unsigned long) sheight);
    regw (SCALE_HACC, 0 );
    regw (SCALE_VACC, 0 );
    
    
    temp = regr (SCALE_X_INC);
    temp &= 0x0F; // first 4 bits are reserved, and should not be touched
    regw (SCALE_X_INC, (temp | (1 << 16)) );
    
    temp = regr (SCALE_Y_INC);
    temp &= 0x0F; // first 4 bits are reserved, and should not be touched
    regw (SCALE_Y_INC, (temp | (1 << 16)) );

    wait_for_fifo (5);
    regw (DP_SRC, 0x00000500 );  // specify fgrd source coming from scaler
    
    // set the approriate destination pix_width, based on display mode
    switch(MODE_INFO.bpp)
    {
        case 16:    if (is_gtb())
                    {
                        temp = 0xB0000B04;
                    }
                    else
                    {
                        temp = 0xC0000C04;
                    }
                    break;
        case 32:    if (is_gtb())
                    {
                        temp = 0xB0000B06;
                    }
                    else
                    {
                        temp = 0xC0000C06;
                    }
                    break;
        default:    break;
    }
     
    regw (DP_PIX_WIDTH, temp );
    regw (DP_WRITE_MSK, 0xFFFFFFFF );
    regw (DP_MIX, 0x00070007 );
    regw (GUI_TRAJ_CNTL, 0x00000003 );
    

    // The source data is at (x1 y1) and has a size of (swidth, sheight).
    // We previously loaded the image at this location (offscreen).
    // The destination of the blit will be drawn at (x2, y2).
    x1 = 0;
    y1 = (MODE_INFO.yres + 10);
    x2 = 0;
    y2 = 0;

    newdwidth = MODE_INFO.xres;
    newdheight = MODE_INFO.yres;

    // compute new scaling factors
    scalex = 65536.0 * swidth/newdwidth;
    scaley = 65536.0 * sheight/newdheight;

    wait_for_fifo (6);

    temp = regr (SCALE_X_INC);
    temp &= 0x0F; // first 4 bits are reserved, and should not be touched
    scalex = (unsigned long) scalex & 0xFFFFFFF0;
    temp = temp | (unsigned long) scalex;
    regw (SCALE_X_INC, temp);
            
    temp = regr (SCALE_Y_INC);
    temp &= 0x08; // first 4 bits are reserved, and should not be touched
    scaley = (unsigned long) scaley & 0xFFFFFFF0;
    temp = temp | (unsigned long) scaley;
    regw (SCALE_Y_INC, temp);

    
    do
    {

        switch(input)
        {
    
        // for blending
        case 'b':
        case 'B':   wait_for_fifo (1);
                    if (BLEND_OR_REPLICATE)
                    {
                        regw (SCALE_3D_CNTL, 0x00000040);
                        BLEND_OR_REPLICATE = 0;
                    }
                    else
                    {
                        regw (SCALE_3D_CNTL, 0x00000140);
                        BLEND_OR_REPLICATE = 1;
                    }
                    
                    break;
        

        case 'i':
        case 'I':   INVERT = !INVERT;
                    wait_for_fifo (1);
                    if(INVERT)
                    {
                        regw (GUI_TRAJ_CNTL, 0x00000001);
                    }
                    else
                    {
                        regw (GUI_TRAJ_CNTL, 0x00000003);
                    }
                    
                    break;                    

        // for extended codes, we need to getch again after receiving
        // the zero.
        case 0:     extcode = getch();
                    switch(extcode)
                    {
                    case UP_ARROW:      
                    case DOWN_ARROW:    if ( (newdheight <= MODE_INFO.yres) && (extcode==DOWN_ARROW) )
                                        {
                                            newdheight += MODE_INFO.yres/10;
                                        }
                                        else
                                        {
                                            newdheight -= MODE_INFO.yres/10;
                                        }

                                        // make sure it's not taller than screen
                                        if (newdheight > MODE_INFO.yres)
                                        {
                                            newdheight = MODE_INFO.yres;
                                        }

                                        // make sure it's not too narrow as well
                                        if (newdheight < (MODE_INFO.yres/10))
                                        {
                                            newdheight = MODE_INFO.yres/10;
                                        }
                                        
                                        // calculate new scaling factor
                                        scaley = 65536.0 * sheight/newdheight;

                                        wait_for_fifo(2);
                                        temp = regr (SCALE_Y_INC);
                                        temp &= 0x0F; // first 4 bits are reserved, and should not be touched
                                        scaley = (unsigned long) scaley & 0xFFFFFFF0;
                                        temp = temp | (unsigned long) scaley;
                                        regw (SCALE_Y_INC, temp);

                                        break;
                                        
                    case RIGHT_ARROW:
                    case LEFT_ARROW:    if ( (newdwidth <= MODE_INFO.xres) && (extcode==RIGHT_ARROW) )
                                        {
                                            newdwidth += MODE_INFO.xres/10;
                                        }
                                        else
                                        {
                                            newdwidth -= MODE_INFO.xres/10;
                                        }

                                        // make sure it's not wider than screen
                                        if (newdwidth > MODE_INFO.xres)
                                        {
                                            newdwidth = MODE_INFO.xres;
                                        }

                                        // make sure it's not too thin as well
                                        if (newdwidth < (MODE_INFO.xres/10))
                                        {
                                            newdwidth = MODE_INFO.xres/10;
                                        }
                                        
                                        // calculate new scaling factor
                                        scalex = 65536.0 * swidth/newdwidth;

                                        wait_for_fifo (2);
                                        temp = regr (SCALE_X_INC);
                                        temp &= 0x0F; // first 4 bits are reserved, and should not be touched
                                        scalex = (unsigned long) scalex & 0xFFFFFFF0;
                                        temp = temp | (unsigned long) scalex;
                                        regw (SCALE_X_INC, temp);

                                        break;


                    default:            break;
                    }


            default:    break;
            } // switch
        

        // do the blt
        wait_for_fifo (2);

        regw ( SCALE_HACC, 0 ); // zero out the accumulators
        regw ( SCALE_VACC, 0 );
        
        if(INVERT)
        {
            regw (GUI_TRAJ_CNTL, 0x00000003);
        }

        clear_screen (0, 0, MODE_INFO.xres, MODE_INFO.yres);

        if (INVERT)
        {
            regw (GUI_TRAJ_CNTL, 0x00000001);
        }

        s_blit ( 0, 0, newdwidth, newdheight);

        input=getch();
    
    }
    while(input!=27);

    
    // Batch command to restore old mode.

    finish ();
    exit (0);                           // No errors.

} // main


/******************************************************************************
 * s_blit                                                                     *
 *  Function: Performs a simple or scaled screen to screen blit               *
 *    Inputs: x1 - top left source x coordinate in pixels                     *
 *            y1 - top left source y coordinate in pixels                     *
 *            x2 - top left destination x coordinate in pixels                *
 *            y2 - top left destination y coordinate in pixels                *
 *            dwidth - destination width                                      *
 *            dheight - destination height                                    *
 *   Outputs: NONE                                                            *
 *     Notes: Copy the contents of screen memory at (x1, y1) of size          *
 *            (width x height) to (x2, y2) using the current engine           *
 *            settings. This is known as an unbounded Y source                *
 *            trajectory blit. For 24 bpp modes, the engine is in 8 bpp       *
 *            and the CRTC is in 24 bpp. For this reason, all horizontal      *
 *            parameters must be mulitplied by 3. The blit source is          *
 *            determined by the current setting of the DP_SRC register.       *
 ******************************************************************************/
void s_blit (int x, int y, int dwidth, int dheight)
{

    // Set up the destination parameters for the blit.
    wait_for_fifo (4);
    
    regw (DST_X, x);

    if(INVERT)
    {
        regw (DST_Y, y + dheight - 1);
    }
    else
    {
        regw (DST_Y, y);
    }

    regw (DST_HEIGHT, dheight); // height of image
    regw (DST_WIDTH, dwidth); // width of image

    return;
    
} // s_blit
