/******************************************************************************
 * hwcursor.c - chapter 6 sample code                                         *
 *                                                                            *
 * To be used with mach64 sample code.                                        *
 * This module conatins primitive dealing with the hardware cursor.           *
 * Functions define, enable, disable, and position the hardware cursor.       *
 *                                                                            *
 * Copyright (c) 1994-1998 ATI Technologies Inc.  All rights reserved.        *
 ******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include "atim64.h"
#include "defines.h"
#include "main.h"

/******************************************************************************
 * set_hwcursor                                                               *
 *  Function: sets up the hardware cursor data region according to the        *
 *            given bitmap data.  The data region is located at (0,y).        *
 *    Inputs: y - y location of cursor bitmap - usually in offscreen mem      *
 *            width - width of cursor                                         *
 *            height - height of cursor                                       *
 *            hot_x - x coordinate of cursor hot spot                         *
 *            hot_y - y coordinate of cursor hot spot                         *
 *            colour0 - cursor colour 0                                       *
 *            colour1 - cursor colour 1                                       *
 *            *bitmap - pointer to bitmap of cursor                           *
 *   Outputs: NONE                                                            *
 *     Notes: The hardware cursor be vary in size from 1x1 to 64x64.          *
 *            The expected bitmap format is LSB to MSB. The LSB will be       *
 *            drawn first in a left to right direction. Since the cursor      *
 *            position is NOT set in this routine, the position should        *
 *            be set (set_hwcursor_pos) prior to enabling the cursor.         *
 ******************************************************************************/

void set_hwcursor (int y, int width, int height, int hot_x, int hot_y,
                   unsigned long colour0, unsigned long colour1,
                   unsigned int *bitmap)
{
    unsigned long cur_offset, cur_size, cur_pitch;
    unsigned long temp1, temp2, temp3, temp4, temp5, temp6;
    unsigned long bitdata;
    int i, index, dataindex, start, widthwords, temp;

    // Check that cursor size is within limits
    if ((width < 1) || (width > 64)) return;
    if ((height < 1) || (height > 64)) return;
    if ((hot_x < 0) || (hot_x >= width)) return;
    if ((hot_y < 0) || (hot_y >= height)) return;

    // Set cursor dimensions.
    CURSORDATA.y = y;
    CURSORDATA.width = width;
    CURSORDATA.height = height;
    CURSORDATA.hot_x = hot_x;
    CURSORDATA.hot_y = hot_y;
    CURSORDATA.colour0 = colour0;
    CURSORDATA.colour1 = colour1;

    // Set hardware cursor bitmap to transparent.
    for (index = 0; index < (HWCURHEIGHT * HWCURWIDTH); index++)
    {
        CURSORDATA.bitmap[index] = 0xAAAA;
    } // for

    // Load user hardware cursor data into bitmap.
    dataindex = 0;
    widthwords = width / 8;
    if (width > widthwords * 8)
    {
        widthwords++;
    } // if
    start = HWCURWIDTH - widthwords;
    for (index = start; index < (HWCURWIDTH * height); index += HWCURWIDTH)
    {
        i = 0;
        do
        {
            CURSORDATA.bitmap[index + i] = *(bitmap + dataindex);
            if (width < 8)
            {
                CURSORDATA.bitmap[index + i] = CURSORDATA.bitmap[index + i] <<
                                               ((8 - width) * 2);
            } // if
            dataindex++;
            i++;
        } while (i < widthwords);
    } // for

    // Calculate offset of coordinate (0, y) in qwords.
    CURSORDATA.cur_offset = cur_offset = get_xy_offset (0, y) / 8;

    // Calculate cursor pitch (assuming 16 bpp).
    cur_pitch = HWCURWIDTH / 8;
    cur_pitch = cur_pitch << 22;

    // Write mach64 hardware cursor to video memory.
    // Use 16 bpp to setup hardware cursor bitmap.

    // Save vital registers.
    wait_for_idle ();
    temp1 = regr (DP_PIX_WIDTH);
    temp2 = regr (DP_CHAIN_MASK);
    temp3 = regr (DST_OFF_PITCH);
    temp4 = regr (DP_SRC);
    temp5 = regr (DP_MIX);
    temp6 = regr (DST_CNTL);

    // Load bitmap data to hardware cursor bitmap data area.
    wait_for_fifo (10);
    regw (DP_PIX_WIDTH, (temp1 & 0xFFC00000) | BYTE_ORDER_LSB_TO_MSB |
                        HOST_16BPP | SRC_16BPP | DST_16BPP);
    regw (DP_CHAIN_MASK, 0x0410);       // Chain mask for 16 bpp.
    regw (DST_OFF_PITCH, cur_pitch | cur_offset);
    regw (DP_SRC, FRGD_SRC_HOST);
    regw (DP_MIX, FRGD_MIX_S | BKGD_MIX_D);
    regw (DST_CNTL, DST_Y_TOP_TO_BOTTOM | DST_X_LEFT_TO_RIGHT);
    regw (DST_X, 0);
    regw (DST_Y, 0);
    regw (DST_HEIGHT, HWCURHEIGHT);
    regw (DST_WIDTH, HWCURWIDTH);
    for (index = 0; index < (HWCURHEIGHT * HWCURWIDTH * 2); index += 2)
    {
        wait_for_fifo (1);
        bitdata = (unsigned long) (CURSORDATA.bitmap[index+1]);
        bitdata = (bitdata << 16) | (CURSORDATA.bitmap[index]);
        regw (HOST_DATA0, bitdata);
    } // for
    
    wait_for_idle ();

    // Set cursor size offsets.
    cur_size = (unsigned long) (64 - height);
    cur_size = (unsigned long) ((cur_size << 16) | (64 - width));

    wait_for_fifo (8);

    regw (CUR_HORZ_VERT_OFF, cur_size);

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

    // Restore used registers.
    regw (DP_PIX_WIDTH, temp1);
    regw (DP_CHAIN_MASK, temp2);
    regw (DST_OFF_PITCH, temp3);
    regw (DP_SRC, temp4);
    regw (DP_MIX, temp5);
    regw (DST_CNTL, temp6);

    // Set cursor colours.
    set_cursor_colours (colour0, colour1);

    return;

} // set_hwcursor


/******************************************************************************
 * enable_hwcursor                                                            *
 *  Function: turns on the hardware cursor                                    *
 *    Inputs: NONE                                                            *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void enable_hwcursor (void)
{
    // Enable hardware cursor.
    iow16 (GEN_TEST_CNTL, ior16 (GEN_TEST_CNTL) | HWCURSOR_ENABLE);

    return;
    
} // enable_hwcursor


/******************************************************************************
 * disable_hwcursor                                                           *
 *  Function: turns off the hardware cursor                                   *
 *    Inputs: NONE                                                            *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void disable_hwcursor (void)
{
    // Disable hardware cursor.
    iow16 (GEN_TEST_CNTL, ior16 (GEN_TEST_CNTL) & (~HWCURSOR_ENABLE));

    return;
    
} // disable_hwcursor


/******************************************************************************
 * set_hwcursor_pos                                                           *
 *  Function: sets the hardware cussor position                               *
 *    Inputs: x - x coordinate of position                                    *
 *            y - y coordinate of position                                    *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void set_hwcursor_pos (int x, int y)
{
    unsigned long cur_pos, cur_size, cur_offset;

    // Check for coordinate violations.
    if (x < 0) x = 0;
    if (y < 0) y = 0;
    CURSORDATA.current_x = x;
    CURSORDATA.current_y = y;

    // Set the cursor position such that the (x, y) coordinates equal the
    // hot spot.
    if (y < CURSORDATA.hot_y)
    {
        // Adjust CUR_OFFSET to point to (hot_y - y) lines into the
        // cursor data region. Also increase CUR_VERT_OFFSET by the
        // same number of lines that CUR_OFFSET was adjusted by. Since
        // these two registers are being used to set the cursor position,
        // set CUR_VERT_POSN to zero.

        cur_size = (unsigned long) (64 - CURSORDATA.height +
                                        CURSORDATA.hot_y - y);
        cur_pos = 0;
        cur_offset = CURSORDATA.cur_offset +
                     (unsigned long) ((CURSORDATA.hot_y - y) * 2);
    }
    else
    {
        // Apply normal cursor defining parameters when y >= hot_y.
        cur_size = (unsigned long)(64 - CURSORDATA.height);
        cur_pos = (unsigned long)(y - CURSORDATA.hot_y);
        cur_offset = CURSORDATA.cur_offset;
    } // if

    // Shift up Y quantities by 16 bits as they are in the upper word for
    // each cursor register.
    cur_size = cur_size << 16;
    cur_pos = cur_pos << 16;

    if (x < CURSORDATA.hot_x)
    {
        // Set the cursor position by adding (hot_x - x) cursor pixels to
        // CUR_HORZ_OFFSET. As in the Y coordinate case, set
        // CUR_HORZ_POSN to zero.

        cur_size = (unsigned long) (cur_size | (64 - CURSORDATA.width +
                                                CURSORDATA.hot_x - x));
    }
    else
    {
        // Apply normal cursor defining parameters when x >= hot_x.

        cur_size = (unsigned long) (cur_size | (64 - CURSORDATA.width));
        cur_pos = (unsigned long) (cur_pos | (x - CURSORDATA.hot_x));
    } // if

    // Write cursor positioning parameters to the cursor registers.
    regw (CUR_OFFSET, cur_offset);
    regw (CUR_HORZ_VERT_OFF, cur_size);
    regw (CUR_HORZ_VERT_POSN, cur_pos);

    return;
    
} // set_hwcursor_pos


/******************************************************************************
 * get_hwcursor_pos                                                           *
 *  Function: gets the hardware cursor position                               *
 *    Inputs: NONE                                                            *
 *   Outputs: *position - x, y coordinate                                     *
 ******************************************************************************/

void get_hwcursor_pos (point *position)
{
    position->x = CURSORDATA.current_x;
    position->y = CURSORDATA.current_y;

    return;

} // get_hwcursor_pos


/******************************************************************************
 * set_cursor_colours                                                         *
 *  Function: sets hardware cursor colours                                    *
 *    Inputs: colour0 - cursor colour 0                                       *
 *            colour1 - cursor colour 1                                       *
 *   Outputs: NONE                                                            *
 *     Notes: This function is called exclusively by set_hwcursor()           *
 ******************************************************************************/

void set_cursor_colours (unsigned long colour0, unsigned long colour1)
{
    unsigned long curclr0, curclr1;
    unsigned long red0, green0, blue0, red1, green1, blue1;
    unsigned long rshift, gshift, bshift;
    unsigned long temp;
    int index;
    palette entry;

    // Setup cursor colours for 4, 8 bpp modes.
    if ((MODE_INFO.bpp == 4) || (MODE_INFO.bpp == 8))
    {
        // Get colour componets from palette using colour indices.

        if ((QUERY_DATA.dac_type == DAC_ATI68860_1) ||
            (QUERY_DATA.dac_type == DAC_ATI68860_2) ||
            (QUERY_DATA.dac_type == DAC_IBM514) ||
            (QUERY_DATA.dac_type == DAC_INTERNAL) ||
            (ior16 (CFG_CHIP_TYPE) == CHIP_CT_ID))
        {

            // Cursor colours are 8 bit values,
            // palette colours are 6 bit values.

            entry = get_palette ((int) (colour0 & 0xFF));
            red0 = (unsigned long) (entry.red) << 2;
            green0 = (unsigned long) (entry.green) << 2;
            blue0 = (unsigned long) (entry.blue) << 2;
            entry = get_palette ((int) (colour1 & 0xFF));
            red1 = (unsigned long) (entry.red) << 2;
            green1 = (unsigned long) (entry.green) << 2;
            blue1 = (unsigned long) (entry.blue) << 2;

            // Assign cursor colour values for mach64 CT internal DACs.

            if ((ior16 (CFG_CHIP_TYPE) == CHIP_CT_ID) ||
               (QUERY_DATA.dac_type == DAC_INTERNAL))
            {
                curclr0 = (red0 << 24) | (green0 << 16) | (blue0 << 8) | colour0;
                curclr1 = (red1 << 24) | (green1 << 16) | (blue1 << 8) | colour1;
            } // if
        }
        else
        {
            // Use colour indices for standard DACs.

            curclr0 = (int) (colour0 & 0xFF);
            curclr1 = (int) (colour1 & 0xFF);
        } // if
    }
    else
    {
        // Setup cursor colour for 15, 16, 24, 32 bpp modes.
        // Determine colour component shifting values.

        switch (MODE_INFO.bpp)
        {
            case 15:
                rshift = 3;
                gshift = 3;
                bshift = 3;
                break;

            case 16:
                rshift = 3;
                gshift = 2;
                bshift = 3;
                break;

            case 24:
            case 32:
                rshift = 0;
                gshift = 0;
                bshift = 0;
                break;
        } // switch

        // Get cursor colour 0 components.

        red0 = get_primary_colour (RED, colour0) << rshift;
        green0 = get_primary_colour (GREEN, colour0) << gshift;
        blue0 = get_primary_colour (BLUE, colour0) << bshift;

        // Get cursor colour 1 components.

        red1 = get_primary_colour (RED, colour1) << rshift;
        green1 = get_primary_colour (GREEN, colour1) << gshift;
        blue1 = get_primary_colour (BLUE, colour1) << bshift;

        // Assign cursor colour values for standard DACs.

    } // if


    // Write to hardware to set cursor colours for standard DACs.
    regw (CUR_CLR0, curclr0);
    regw (CUR_CLR1, curclr1);

    return;

} // set_cursor_colours
