/******************************************************************************
 * mach64 Chapter 6 sample code                                               *
 *                                                                            *
 * gpline.c - This program uses the mach64 engine to perform a general        *
 * pattern bitblit.  (source trajectory 4 - General Pattern)                  *
 *                                                                            *
 * Copyright (c) 1994-1998 ATI Technologies Inc.  All rights reserved.        *
 ******************************************************************************/

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

// Prototypes.

void host_fill (int src_line_length);

/******************************************************************************
 * Main Program to demonstrate a simple general pattern bitblits              *
 *  Function: Draws a general pattern in the top left corner of the           *
 *            screen.  This pattern is used as the source to fill a few       *
 *            lines.                                                          *
 *    Inputs: Arguments for mode spatial and colour resolution                *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void main (int argc, char *argv[])
{
    int src_line_length, dst_line_length;
    int srcx, srcy;
    int dstx1, dsty1;
    int dstx2, dsty2;

    printf ("mach64 Chapter 6 sample code\n"
            "\n"
            "gpline.c\n"
            "This program demonstrates a general pattern lines.  A general pattern\n"
            "is drawn in the top left corner of the screen.  This pattern is used\n"
            "to fill a few lines on the screen.\n"
            "\n"
            "Spatial resolution (640, 800, 1024, 1280, 1600) and Colour Depth\n"
            "(8, 15, 16, 24, 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.

    start (argc, argv);

    // Check for 24 bpp mode - Lines are not supported in 24 bpp modes.
    if (MODE_INFO.bpp == 24)
    {
        // Disable accelerator mode and switch back to VGA text mode.

        finish ();

        printf ("Pattern lines are not supported in 24 bpp modes.\n");
        exit (1);
    } // if

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

    // Setup line source, destination, and lengths.

    // Source line (a rectangle with a height of 1) at (srcx, srcy).
    srcx = 0;
    srcy = 0;
    src_line_length = MODE_INFO.xres / 20;

    // First line starting coordinate at (dstx1, dsty1).
    dstx1 = MODE_INFO.xres / 40;
    dsty1 = MODE_INFO.yres / 30;

    // Second line starting coordinate at (dstx2, dsty2).
    dstx2 = MODE_INFO.xres / 20;
    dsty2 = MODE_INFO.yres / 30;

    // Destination lines length.
    dst_line_length = MODE_INFO.xres / 2;

    // Draw patterned lines using a source pattern:
    //
    //    A colour source pattern is drawn at (srcx, srcy) and has a length
    //    of src_line_length pixels. Using this colour source pattern, two
    //    patterned lines are drawn at (dstx1, dsty1) and (dstx2, dsty2) with
    //    a length of dst_line_length pixels. The first line is drawn by
    //    reading the source pattern from left to right, and the second line
    //    is drawn by reading the source pattern from right to left. For each
    //    direction, when the end of the colour source pattern is reached, it
    //    will wrap to the start until the entire patterned line is drawn.

    // Setup colour source line (rectangle with height of 1):
    //
    //    Setup a colour source pattern from host data. The number of host
    //    data writes will depend on the current pixel depth. Also, the host
    //    data must be packed into 32 bit pieces (i.e. for 8bpp modes, each
    //    host write draws 4 pixels).

    // Setup a rectangle fill with host data.
    wait_for_fifo (6);
    regw (DST_CNTL, DST_Y_TOP_TO_BOTTOM | DST_X_LEFT_TO_RIGHT);
    regw (DP_SRC, FRGD_SRC_HOST);
    regw (DST_X, srcx);
    regw (DST_Y, srcy);
    regw (DST_HEIGHT, 1);
    regw (DST_WIDTH, src_line_length);

    // Fill source rectangle (height of 1) with packed host data.
    host_fill (src_line_length);

    // Ensure that host transfer is done.
    wait_for_idle ();

    // Draw a diagonal line using data from the source. When the src data
    // runs out, the source pointer will wrap and repeat according to
    // the source x direction line bit in SRC_CNTL. Both directions are
    // demonstrated.

    // Draw line using LEFT TO RIGHT source x direction.

    // Set source registers to point to (srcx, srcy).
    wait_for_fifo (13);
    regw (DP_SRC, FRGD_SRC_BLIT);
    regw (SRC_CNTL, SRC_LINE_X_LEFT_TO_RIGHT | SRC_PATTERN_ENABLE);
    regw (SRC_X, srcx);
    regw (SRC_Y, srcy);
    regw (SRC_HEIGHT1, 1);
    regw (SRC_WIDTH1, src_line_length);

    // Draw a diagonal line at (dstx1, dsty1) of length dst_line_length.
    regw (DST_CNTL, DST_LAST_PEL | DST_Y_TOP_TO_BOTTOM | DST_X_LEFT_TO_RIGHT);
    regw (DST_BRES_ERR, 0);
    regw (DST_BRES_INC, 1);
    regw (DST_BRES_DEC, 0x3FFFF);       // -1.
    regw (DST_X, dstx1);
    regw (DST_Y, dsty1);

    // Src data will repeat.
    regw (DST_BRES_LNTH, dst_line_length);

    // Draw line using RIGHT TO LEFT source x direction.

    // Set source registers to point to (srcx + src_line_length - 1, srcy)
    // since the source will be read from right to left.
    wait_for_fifo (6);
    regw (SRC_CNTL, SRC_LINE_X_RIGHT_TO_LEFT | SRC_PATTERN_ENABLE);
    regw (SRC_X, srcx + src_line_length - 1);
    regw (SRC_Y, srcy);

    // Draw a diagonal line at (dstx2, dsty2) of length dst_line_length.
    regw (DST_X, dstx2);
    regw (DST_Y, dsty2);
    regw (DST_BRES_LNTH, dst_line_length);

    // Wait for a carriage return.
    getch ();

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

    exit (0);                           // No errors.

} // main


/******************************************************************************
 * host_fill                                                                  *
 *  Function: This routine writes data to the screen (for a previously        *
 *            set rectangle fill).  The data is packed into 32 bit pieces     *
 *            since each host register write requires 32 bits of data.        *
 *    Inputs: src_line_length - length of data.                               *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void host_fill (int src_line_length)
{
    int loop, colour, colourcount, colourcycle;
    int shifts, maxshifts, remainder;
    unsigned long hostdata, pixel;
    unsigned long shifter, totalbits;
    unsigned long hostwrts;

    // Determine if there is an even number of host writes - if not, the
    // remainder must be writen to a host register outside the main loop.
    // The extra bits not used will be thrown away by the engine. Also, this
    // insures that the engine will not be busy waiting for more host data.

    remainder = 0;
    totalbits = (unsigned long) (src_line_length) *
                (unsigned long) (MODE_INFO.bpp);
    hostwrts = totalbits / 32;
    if ((hostwrts * 32) != totalbits)
    {
        remainder = 1;
    } // if

    // Main loop for pixel output. In the loop, a host write is done only
    // when the correct number of pixels has accumulated to 32 bits. The
    // data accumulated consists of different colours, changing every 4
    // pixels. This is done to show the data more clearly when displayed
    // on the screen.

    colourcycle = 4;

    colourcount = 0;
    colour = LIGHTBLUE;
    maxshifts = 32 / MODE_INFO.bpp;
    shifts = 0;
    shifter = 0;
    hostdata = 0;
    for (loop = 0; loop < src_line_length; loop++)
    {
        // Pack host data by accumulating 32 bits at a time.

        pixel = get_colour_code (colour);
        pixel = pixel << shifter;
        hostdata = hostdata | pixel;
        shifter = shifter + (unsigned long) (MODE_INFO.bpp);
        shifts++;
        if (shifts >= maxshifts)
        {
            wait_for_fifo (1);
            regw (HOST_DATA0, hostdata);

            shifter = 0;
            shifts = 0;
            hostdata = 0;
        } // if

        // Change colour every 'colourcycle' pixels.

        colourcount++;
        if (colourcount >= colourcycle)
        {
            colourcount = 0;
            colour++;
            if (colour > WHITE)
            {
                colour = LIGHTBLUE;
            } // if
        } // if
    } // for

    // If the total number of bits to be written does not divide evenly by
    // 32, the remaining bits will not be written in the loop. In this case,
    // the remaining bits accumulated during the loop are written through
    // host register below.

    if (remainder == 1)
    {
        wait_for_fifo (1);
        regw (HOST_DATA0, hostdata);
    } // if

    return;

} // host_fill
