/******************************************************************************
 * draw.c - Chapter 6 sample code                                             *
 *                                                                            *
 * To be used with mach64 sample code.                                        *
 * This module contains sample base drawing routines.  Included are           *
 * routines to clear the screen, draw rectangles, draw lines, and             *
 * perform simple blits.                                                      *
 *                                                                            *
 * Copyright (c) 1994-1998 ATI Technologies Inc.  All rights reserved.        *
 ******************************************************************************/

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

/******************************************************************************
 * draw_rectangle                                                             *
 *  Function: uses the mach64 engine to draw a rectangle.                     *
 *            A filled rectangle is drawn at (x, y) of size (width x          *
 *            height) using the current engine settings. For 24 bpp           *
 *            modes, the engine is actually in 8 bpp mode and the CRTC        *
 *            is in 24 bpp. For this reason, all horizontal parameters        *
 *            must be multiplied by 3. Also, the 24 bpp alignment must        *
 *            be determine for the engine to draw pixels in the correct       *
 *            colour. Note that for 24 bpp modes, the input parameter         *
 *            for this routine are in 24 bpp. The rectangle source is         *
 *            determined by the current setting of the DP_SRC register.       *
 *    Inputs: x - starting x coordinate in pixels (left most)                 *
 *            y - starting y coordinate in pixels (top most)                  *
 *            width - width of rectangle in pixels                            *
 *            height - height of rectangle in pixels                          *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void draw_rectangle (int x, int y, int width, int height)
{
    unsigned long temp, rotation;

    // Wait for idle before reading GUI registers.
    wait_for_idle ();

    // Save used registers.
    temp = regr (DST_CNTL);

    if (MODE_INFO.bpp == 24)
    {
        // Set 24 bpp alignment while maintaining direction bits. The
        // argument to this macro must be a 24bpp value.

        rotation = (unsigned long) (((x * 3) / 4) % 6);

        // Adjust horizontal parameters. Note that this adjustment is
        // performed AFTER calling the GET24BPPROTATION macro, as this
        // macro expects unadjusted 24 bpp values.

        x = x * 3;
        width = width * 3;

        regw (DST_CNTL, (temp & 0xDF) | DST_24_ROTATION_ENABLE |
                        (rotation << 8));
    } // if

    // Perform rectangle fill.
    regw (DST_X, (unsigned long) x);
    regw (DST_Y, (unsigned long) y);
    regw (DST_HEIGHT, (unsigned long) height);
    regw (DST_WIDTH, (unsigned long) width);

    // Restore.
    regw (DST_CNTL, temp);

    return;

} // draw_rectangle


/******************************************************************************
 * clear_screen                                                               *
 *  Function: uses the mach64 engine clear the screen.  The routine           *
 *            draw_rectangle is used, along with setting the mix to '0'.      *
 *    Inputs: x - starting x coordinate in pixels (left most) (0)             *
 *            y - starting y coordinate in pixels (top most)  (0)             *
 *            width - width of clear area in pixels           (xres)          *
 *            height - height of clear area in pixels         (yres)          *
 *   Outputs: NONE                                                            *
 *     Notes: values in brackets to clear entire screen                       *
 ******************************************************************************/

void clear_screen (int x, int y, int width, int height)
{
    unsigned long temp;

    // Wait for idle before reading GUI registers.
    wait_for_idle ();

    // Save used registers.
    temp = regr (DP_MIX);

    // Perform clear screen.
    regw (DP_MIX, FRGD_MIX_ZERO | BKGD_MIX_ZERO);
    draw_rectangle (x, y, width, height);

    // Wait for clear screen operation to finish before exiting.
    wait_for_idle ();

    // Restore DP_MIX register.
    regw (DP_MIX, temp);

    return;

} // clear_screen


/******************************************************************************
 * draw_line                                                                  *
 *  Function: draws a line from (x1, y1) to (x2, y2) using the Bresenham      *
 *            algorithm and the mach64 engine.                                *
 *    Inputs: x1 - starting x coordinate in pixels                            *
 *            y1 - starting y coordinate in pixels                            *
 *            x2 - ending x coordinate in pixels                              *
 *            y2 - ending y coordinate in pixels                              *
 *   Outputs: NONE                                                            *
 *     Notes: The drawing of the last pixel in the line is determined         *
 *            by the current setting of the DST_CNTL register (LAST_PEL       *
 *            bit).                                                           *
 *            24 bpp uses a special routine.                                  *
 ******************************************************************************/

void draw_line (int x1, int y1, int x2, int y2)
{
    int dx, dy;
    int small, large;
    int x_dir, y_dir, y_major;
    unsigned long err, inc, dec, temp;

    // Call specific routine if mode is in 24 bpp.
    if (MODE_INFO.bpp == 24)
    {
        draw_line24 (x1, y1, x2, y2);
        return;
    } // if

    // Determine x & y deltas and x & y direction bits.
    if (x1 < x2)
    {
        dx = x2 - x1;
        x_dir = 1;
    }
    else
    {
        dx = x1 - x2;
        x_dir = 0;
    } // if

    if (y1 < y2)
    {
        dy = y2 - y1;
        y_dir = 2;
    }
    else
    {
        dy = y1 - y2;
        y_dir = 0;
    } // if

    // Determine x & y min and max values; also determine y major bit.
    if (dx < dy)
    {
        small = dx;
        large = dy;
        y_major = 4;
    }
    else
    {
        small = dy;
        large = dx;
        y_major = 0;
    } // if

    // Calculate Bresenham parameters and draw line.
    err = (unsigned long) ((2 * small) - large);
    inc = (unsigned long) (2 * small);
    dec = 0x3FFFF - ((unsigned long) (2 * (large - small)));

    // Wait for idle before reading GUI registers.
    wait_for_idle ();

    // Save used registers.
    temp = regr (DST_CNTL);

    // Draw Bresenham line.
    regw (DST_X, (unsigned long) x1);
    regw (DST_Y, (unsigned long) y1);

    // Allow setting of last pel bit and polygon outline bit for line drawing.
    regw (DST_CNTL, (temp & 0x60) | (unsigned long) (y_major | y_dir | x_dir));
    regw (DST_BRES_ERR, err);
    regw (DST_BRES_INC, inc);
    regw (DST_BRES_DEC, dec);
    regw (DST_BRES_LNTH, (unsigned long) (large + 1));

    // Restore.
    regw (DST_CNTL, temp);

    return;

} // draw_line


/******************************************************************************
 * draw_line24                                                                *
 *  Function: draws a line from (x1, y1) to (x2, y2) using the Bresenham      *
 *            algorithm and the mach64 engine for 24bpp modes.                *
 *    Inputs: x1 - starting x coordinate in pixels                            *
 *            y1 - starting y coordinate in pixels                            *
 *            x2 - ending x coordinate in pixels                              *
 *            y2 - ending y coordinate in pixels                              *
 *   Outputs: NONE                                                            *
 *     Notes: Since the engine does not directly support 24 bpp modes,        *
 *            it is set to 8 bpp while the CRTC is set to 24 bpp display      *
 *            mode (RGB). For rectangle drawing, all X coordinates and        *
 *            widths must be converted to 8 bpp sizes. This is done by        *
 *            taking the 24 bpp value and multipling it by 3.                 *
 ******************************************************************************/

void draw_line24 (int x1, int y1, int x2, int y2)
{
    int x, y, xend, yend, dx, dy;
    int d, incr1, incr2, incr3;
    unsigned long rotation, temp1, temp2;

    // Save register.
    wait_for_idle ();
    temp1 = regr (DST_CNTL);
    temp2 = 0xA3;

    // Bresenham line routine.
    dx = abs (x2 - x1);
    dy = abs (y2 - y1);

    // Check slope.
    if (dy <= dx)
    {
        // Slope <= 1.

        if (x1 > x2)
        {
            x = x2;
            y = y2;
            xend = x1;
            dy = y1 - y2;
        }
        else
        {
            x = x1;
            y = y1;
            xend = x2;
            dy = y2 - y1;
        } // if

        d = (2 * dy) - dx;
        incr1 = 2 * dy;
        incr2 = 2 * (dy - dx);
        incr3 = 2 * (dy + dx);

        regw (DST_HEIGHT, 1);
        regw (DST_Y, y);

        do
        {
            wait_for_fifo (4);
            rotation = (unsigned long) (((x*3)/4)%6);
            regw (DST_CNTL, temp2 | (rotation << 8));
            regw (DST_X, x * 3);
            regw (DST_WIDTH, 3);

            x++;

            if (d >= 0)
            {
                if (dy <= 0)
                {
                    d = d + incr1;
                }
                else
                {
                    y++;
                    regw (DST_Y, y);
                    d = d + incr2;
                } // if
            }
            else
            {
                if (dy >= 0)
                {
                    d = d + incr1;
                }
                else
                {
                    y--;
                    regw (DST_Y, y);
                    d = d + incr3;
                } // if
            } // if
        } while (x <= xend);
    }
    else
    {
        // Slope > 1.

        if (y1 > y2)
        {
            y = y2;
            x = x2;
            yend = y1;
            dx = x1 - x2;
        }
        else
        {
            y = y1;
            x = x1;
            yend = y2;
            dx = x2 - x1;
        } // if

        d = (2 * dx) - dy;
        incr1 = 2 * dx;
        incr2 = 2 * (dx - dy);
        incr3 = 2 * (dx + dy);

        regw (DST_HEIGHT, 1);

        do
        {
            wait_for_fifo (3);
            rotation = (unsigned long) (((x*3)/4)%6);
            regw (DST_CNTL, temp2 | (rotation << 8));
            regw (DST_Y_X, ((unsigned long) (x * 3) << 16) | y);
            regw (DST_WIDTH, 3);

            y++;

            if (d >= 0)
            {
                if (dx <= 0)
                {
                    d = d + incr1;
                }
                else
                {
                    x++;
                    d = d + incr2;
                } // if
            }
            else
            {
                if (dx >= 0)
                {
                    d = d + incr1;
                }
                else
                {
                    x--;
                    d = d + incr3;
                } // if
            } // if
        } while (y <= yend);
    } // if

    // Restore register.
    wait_for_fifo (1);
    regw (DST_CNTL, temp1);

    return;

} // draw_line24


/******************************************************************************
 * blit                                                                       *
 *  Function: Performs a simple 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                *
 *            width - width of blits block                                    *
 *            height - height of blits block                                  *
 *   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 blit (short x1, short y1, short x2, short y2, short width, short height)
{
    unsigned long temp, rotation;

    // Wait for idle before reading GUI registers.
    wait_for_idle ();

    // Save used registers.
    temp = regr (DST_CNTL);

    if (MODE_INFO.bpp == 24)
    {
        // Get 24 bpp alignment rotation for destination.
        rotation = (unsigned long) (((x2*3)/4)%6);

        // Adjust horizontal parameters.
        x1 = x1 * 3;
        x2 = x2 * 3;
        width = width * 3;

        // Set 24 bpp alignment while maintaining direction bits.
        regw (DST_CNTL, (temp & 0xDF) | DST_24_ROTATION_ENABLE |
                        (rotation << 8));
    }

    // Perform a blit.
    regw (SRC_X, x1);
    regw (SRC_Y, y1);
    regw (SRC_HEIGHT1, height);
    regw (SRC_WIDTH1, width);

    regw (DST_X, x2);
    regw (DST_Y, y2);
    regw (DST_HEIGHT, height);
    regw (DST_WIDTH, width);

    // Restore.
    regw (DST_CNTL, temp);

    return;

} // blit
