/******************************************************************************
 * hwcursor.c - chapter 6 sample code                                         *
 *                                                                            *
 * To be used with Rage 128 sample code.                                      *
 * This module contains functions dealing with the hardware cursor.           *
 * Functions define, enable, disable, and position the hardware cursor.       *
 * There are also some mouse functions to accept input from a mouse.          *
 *                                                                            *
 * Copyright (c) 1999 ATI Technologies Inc.  All rights reserved.             *
 ******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include <i86.h>
#include "..\util\regdef.h"
#include "..\util\defines.h"
#include "..\util\main.h"

cursor_pos cp;
BYTE USE_MOUSE = 0x0;
BYTE cursor = 0;

DWORD colour_table[16] =
{
    0x00000000,     // Black
    0x0000009E,     // Dark Blue
    0x00009E00,     // Dark Green
    0x00009E9E,     // Dark Cyan
    0x009E0000,     // Dark Red
    0x009E009E,     // Dark Magenta
    0x009E9E00,     // Brown
    0x009E9E9E,     // Light Gray
    0x00555555,     // Dark Gray
    0x000000FF,     // Light Blue
    0x0000FF00,     // Light Green
    0x0000FFFF,     // Light Cyan
    0x00FF0000,     // Light Red
    0x00FF00FF,     // Light Magenta
    0x00FFFF00,     // Yellow
    0x00FFFFFF      // White
};

WORD cwidth [NUM_CURSORS] = { 16, 32, 64, 32 };
WORD cheight [NUM_CURSORS] = { 16, 32, 64, 64 };
BYTE cursorxor[NUM_CURSORS][512];
BYTE cursorand[NUM_CURSORS][512];
hwcursor CURSORDATA[4];


// prototypes
void R128_SetHWCursor (BYTE);
void R128_EnableHWCursor (void);
void R128_DisableHWCursor (void);
void R128_SetHWCursorPos (WORD x, WORD y);
void R128_SetHWCursorColour (WORD frgd, WORD bkgd);
void LoadCursors (void);

/****************************************************************************
 * Main Program to demonstrate the Rage 128 hardware cursor                 *
 *  Function: shows the various properties of the hardware cursor.          *
 *    Inputs: Arguments for mode spatial and colour resolution              *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void main (int argc, char *argv[])
{
    WORD x, y, y_inc, x_inc;
    WORD frgd, whichcur;
    WORD height, width;
    char extcode = '1';
    char input = '1';

    mouse_pos mp;

    // Batch command to detect the Rage 128, save old mode information,
    // process mode info arguments, set mode, initialize engine to known state
    R128_StartUp (argc, argv);

    // Load the cursor bitmaps into offscreen memory.
    LoadCursors ();

    frgd = 0;
    height = R128_AdapterInfo.yres /4;
    width = R128_AdapterInfo.xres /4;

    // create a screen background that uses 16 different coloured
    // rectangles, to show transparency, compliment colours, etc.
    for (x=0;x < 4; x++)
    {
        for (y=0; y<4;y++)
        {
            R128_WaitForFifo (1);
            R128_DrawRectangle (x*width, y*height, width, height, frgd);
            frgd++;
        }
    }

    // Initialize the hardware cursor.
    R128_SetHWCursor (0);
    R128_SetHWCursorColour (WHITE, BLACK);

    // Let's see if a mouse and driver is installed
    USE_MOUSE = MOUSE_InitMouse ();

    // Position the cursor at the center of the screen initially.
    R128_SetHWCursorPos (0, 0);

    // Enable the hardware cursor.
    R128_EnableHWCursor ();

    frgd = 0;
    whichcur = 0;

    if (USE_MOUSE)
    {
        while (!kbhit ())
        {
            MOUSE_UpdatePosition (&mp);
            if (mp.buttons & LEFT_BUTTON_DOWN)
            {
                // left button is down, change cursor colour
                // but wait until the button is back up
                while (mp.buttons & LEFT_BUTTON_DOWN)
                {
                    MOUSE_UpdatePosition (&mp);
                }

                R128_SetHWCursorColour (frgd, 0);
                frgd++;
                if (frgd >= 16)
                {
                    frgd = 0;
                }
            }
            if (mp.buttons & RIGHT_BUTTON_DOWN)
            {
                while (mp.buttons & RIGHT_BUTTON_DOWN)
                {
                    MOUSE_UpdatePosition (&mp);
                }
                whichcur++;
                if (whichcur >= NUM_CURSORS)
                {
                    whichcur = 0;
                }
                R128_SetHWCursor (whichcur);
            }

            R128_SetHWCursorPos (mp.x, mp.y);
            }
            // eat up the keyboard hit
            getch ();
        }
        // no mouse installed, so use keyboard
        else
        {
            cp.x = 0;
            cp.y = 0;
            x_inc = R128_AdapterInfo.xres/50;
            y_inc = R128_AdapterInfo.yres/50;

            do
            {
            switch (input)
            {
            case 'c':
            case 'C':   R128_SetHWCursorColour (frgd, 0);
                        frgd++;
                        if (frgd >= 16)
                        {
                            frgd = 0;
                        }
                        break;
            case 'i':
            case 'I':   whichcur++;
                        if (whichcur >= NUM_CURSORS)
                        {
                            whichcur = 0;
                        }
                        R128_SetHWCursor (whichcur);
                        break;
            case 0:     extcode = getch (); // must get next char after
                                            // receiving extended code (0)
                        switch (extcode)
                        {
                            case UP_ARROW:  cp.y -= y_inc;
                                            if (cp.y <= 0)
                                            {
                                                cp.y = 0;
                                            }
                                            break;

                            case DOWN_ARROW:
                                            cp.y += y_inc;
                                            if (cp.y >= R128_AdapterInfo.yres)
                                            {
                                                cp.y = R128_AdapterInfo.yres - CURSORDATA[0].height;
                                            }
                                            break;

                            case LEFT_ARROW:
                                            cp.x -= x_inc;
                                            if (cp.x <= 0)
                                            {
                                                cp.x = 0;
                                            }
                                            break;

                            case RIGHT_ARROW:
                                            cp.x += x_inc;
                                            if (cp.x >= R128_AdapterInfo.xres)
                                            {
                                                cp.x = R128_AdapterInfo.xres - CURSORDATA[0].width;
                                            }
                                            break;
                            } // end of nested switch

            }  // end of switch statement

            R128_SetHWCursorPos (cp.x, cp.y);
            input = getch ();
            }
            while (input != 32); // end of do-while
        } // end of else {}

    // Disable the hardware cursor.
    R128_DisableHWCursor ();

    // Batch command to restore old mode.
    R128_ShutDown ();

    exit (0);                           // No errors.
} // main

/****************************************************************************
 * R128_SetHWCursor (BYTE cursor)                                           *
 *  Function: Sets up and initializes the hardware cursor values            *
 *    Inputs: BYTE cursor - the cursor number that will be used             *
 *   Outputs: NONE.                                                         *
 ****************************************************************************/
void R128_SetHWCursor (BYTE cursor)
{
    DWORD horz_offset, vert_offset;

    // Check that cursor size is within limits
    if ((CURSORDATA[cursor].width < 1) || (CURSORDATA[cursor].width > 64)) return;
    if ((CURSORDATA[cursor].height < 1) || (CURSORDATA[cursor].height > 64)) return;

    // determine offsets within cursor bitmap
    horz_offset = 64 - CURSORDATA[cursor].width;
    vert_offset = 64 - CURSORDATA[cursor].height;

    CURSORDATA[cursor].cur_offset = R128_AdapterInfo.MEM_BASE + CURSORDATA[cursor].cur_offset;

    // Set cursor size offsets.
    regw (CUR_HORZ_VERT_OFF, (horz_offset << 16) | vert_offset);

    // Set cursor offset to cursor data region.
    regw (CUR_OFFSET, CURSORDATA[cursor].cur_offset);

} // set_hwcursor


/****************************************************************************
 * R128_EnableHWCursor (void)                                               *
 *  Function: enables the hardware cursor, making it visible                *
 *    Inputs: NONE.                                                         *
 *   Outputs: NONE.                                                         *
 ****************************************************************************/
void R128_EnableHWCursor (void)
{
    regw (CRTC_GEN_CNTL, regr (CRTC_GEN_CNTL) | 0x00010000);
    return;

}


/****************************************************************************
 * R128_DisableHWCursor (void)                                              *
 *  Function: Disables the hardware cursor, making it no longer visible     *
 *    Inputs: NONE.                                                         *
 *   Outputs: NONE.                                                         *
 ****************************************************************************/
void R128_DisableHWCursor (void)
{
    regw (CRTC_GEN_CNTL, regr (CRTC_GEN_CNTL) & ~(0x00010000));
    return;
}


/****************************************************************************
 * R128_SetHWCursorPos (WORD x, WORD y)                                     *
 *  Function: Set the x and y position of the hardware cursor               *
 *    Inputs: WORD x - the X position of the cursor                         *
 *            WORD y - the Y position of the cursor                         *
 *   Outputs: NONE.                                                         *
 ****************************************************************************/
void R128_SetHWCursorPos(WORD x, WORD y)
{
    regw (CUR_HORZ_VERT_POSN, (x << 16) | y);
    return;
}

/****************************************************************************
 * R128_SetHWCursorColour (WORD frgd, WORD bkgd)                            *
 *  Function: sets the colour of the hardware cursor                        *
 *    Inputs: WORD frgd - foreground colour of the cursor                   *
 *            WORD bkgd - background colour of the cursor                   *
 *   Outputs: NONE.                                                         *
 ****************************************************************************/
void R128_SetHWCursorColour (WORD frgd, WORD bkgd)
{
    // Set cursor colour.
    regw (CUR_CLR1, colour_table[frgd]);
    regw (CUR_CLR0, colour_table[bkgd]);
    return;

}


/****************************************************************************
 * LoadCursors (void)                                                       *
 *  Function: loads the cursor XOR and AND planes for the hardware cursor   *
 *            icons into display memory.                                    *
 *    Inputs: NONE.                                                         *
 *   Outputs: NONE.                                                         *
 ****************************************************************************/
void LoadCursors (void)
{
    DWORD x, y, bytepp;
    DWORD *data;
    WORD index, loop;
    FILE *xor0, *xor1, *xor2, *xor3, *and0, *and1, *and2, *and3;

    // open cursor data files (ICN) and load them into memory
    // xor icn files are the XOR plane for the hw cursor
    // and icn files are the AND plane for the hw cursor
    xor0 = fopen ("smarrowx.icn", "r");
    xor1 = fopen ("handxor.icn", "r");
    xor2 = fopen ("cirxor.icn", "r");
    xor3 = fopen ("bgarrowx.icn", "r");
    and0 = fopen ("smarrowa.icn", "r");
    and1 = fopen ("handand.icn", "r");
    and2 = fopen ("cirand.icn", "r");
    and3 = fopen ("bgarrowa.icn", "r");

    // read the files into arrays
    fread (CURSORDATA[0].cursorxor, sizeof(BYTE), 512, xor0);
    fread (CURSORDATA[1].cursorxor, sizeof(BYTE), 512, xor1);
    fread (CURSORDATA[2].cursorxor, sizeof(BYTE), 512, xor2);
    fread (CURSORDATA[3].cursorxor, sizeof(BYTE), 512, xor3);

    fread (CURSORDATA[0].cursorand, sizeof(BYTE), 512, and0);
    fread (CURSORDATA[1].cursorand, sizeof(BYTE), 512, and1);
    fread (CURSORDATA[2].cursorand, sizeof(BYTE), 512, and2);
    fread (CURSORDATA[3].cursorand, sizeof(BYTE), 512, and3);

    // close the files, as we're done with them.
    fclose (xor0);
    fclose (xor1);
    fclose (xor2);
    fclose (xor3);
    fclose (and0);
    fclose (and1);
    fclose (and2);
    fclose (and3);

    // copy cursor widths and heights into CURSORDATA structures
    for (x=0; x<NUM_CURSORS; x++)
    {
        CURSORDATA[x].width = cwidth[x];
        CURSORDATA[x].height = cheight[x];

        // Check if we are in a double scan mode.
        // We must double the height if so.  Also, the maximum height
        // of a cursor in double scanned modes is 32 pixels.  Thus,
        // any cursors larger than 32 pixels high will not be displayed.
        if (regr(CRTC_GEN_CNTL) & 0x1)
        {
            CURSORDATA[x].height *= 2;
        }

    }

    // Move cursor bitmaps into display memory, at 10 lines after the end of display memory
    // Calculate display memory offset where we will write the data.
    x = R128_AdapterInfo.xres;
    y = R128_AdapterInfo.yres;
    bytepp = R128_AdapterInfo.bpp/8;
    if (R128_AdapterInfo.bpp%8)
    {
        bytepp++;
    }

    CURSORDATA[0].cur_offset = x*y*bytepp + (10*x*bytepp);
    // make sure that this location is 128 byte aligned.  If not, move it up accordingly
    x = CURSORDATA[0].cur_offset%4;
    if (x != 0)
    {
        CURSORDATA[0].cur_offset += (4-x);
    }

    CURSORDATA[1].cur_offset = CURSORDATA[0].cur_offset + 1024;
    CURSORDATA[2].cur_offset = CURSORDATA[1].cur_offset + 1024;
    CURSORDATA[3].cur_offset = CURSORDATA[2].cur_offset + 1024;

    for (loop = 0; loop < NUM_CURSORS; loop++)
    {
        y = 0;
        for (index=0; index < 1024; index+=16)
        {
            data = (DWORD *) (R128_AdapterInfo.virtual_MEM_BASE + CURSORDATA[loop].cur_offset + (DWORD)index);
            for (x=0;x<2;x++)
            {
                data[x+2] = (CURSORDATA[loop].cursorxor[y] << 0) | (CURSORDATA[loop].cursorxor[y+1] << 8) | (CURSORDATA[loop].cursorxor[y+2] << 16) | (CURSORDATA[loop].cursorxor[y+3] << 24);
                data[x] = (CURSORDATA[loop].cursorand[y] << 0) | (CURSORDATA[loop].cursorand[y+1] << 8) | (CURSORDATA[loop].cursorand[y+2] << 16) | (CURSORDATA[loop].cursorand[y+3] << 24);
                y+=4;
            }
        }
    }
    return;

} // LoadCursors ()

