/******************************************************************************
 * mach64 Chapter 7 sample code                                               *
 *                                                                            *
 * polygon.c - This program uses the mach64 engine to draw a polygon          *
 *                                                                            *
 * 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"


/******************************************************************************
 * Main Program to demonstrate simple polygon draw                            *
 *  Function: A simple polygon draw.                                          *
 *    Inputs: Arguments for mode spatial and colour resolution                *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void main (int argc, char *argv[])
{
    unsigned long offset;
    unsigned long colour_depth;
    unsigned long mono_pitch;
    unsigned long save_dst_off_pitch;
    unsigned long save_src_off_pitch;
    int width;                          // Width of polygon bounding box.
    int height;                         // Height of polygon bounding box.
    int box_x;                          // Top left x co-ord of bounding box.
    int box_y;                          // Top left y co-ord of bounding box.
    int x1;                             // Work horse point 1 x co-ord.
    int y1;                             // Work horse point 1 y co-ord.
    int x2;                             // Work horse point 2 x co-ord.
    int y2;                             // Work horse point 2 y co-ord.
    int temp;                           // Temporary variable.
    int xmin;                           // Minimun x co-ord of polygon.
    int xmax;                           // Maximun x co-ord of polygon.
    int ymin;                           // Minimun y co-ord of polygon.
    int ymax;                           // Maximum y co-ord of polygon.
    int i;                              // Counter.
    int xoff;                           // x screen offset for polygon draw.
    int yoff;                           // y screen offset for polygon draw.
    int size_A = 12;                    // Number of points in polygon.
    point let_A[13] = {{0, 200}, {73, 0}, {110, 0}, {150, 200}, {100, 200},
                       {96, 150}, {61, 150}, {72, 110}, {93, 110}, {90, 55},
                       {85, 55}, {40, 200}, {0, 200}};

    printf ("mach64 Chapter 7 sample code\n"
            "\n"
            "polygon.c\n"
            "This program demonstrates a simple polygon draw.\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 - Polygons are not supported in 24 bpp modes.

    if (MODE_INFO.bpp == 24)
    {
        // Disable accelerator mode and switch back to VGA text mode.
        finish ();
        printf ("Polygons are not supported in 24 bpp modes.\n");
        exit (1);
    } // if

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

    // Determine width, height, x & y offsets.

    xmin = xmax = let_A[0].x;
    ymin = ymax = let_A[0].y;
    for (i = 1; i < size_A; i++)
    {
        xmin = (xmin < let_A[i].x) ? xmin : let_A[i].x;
        xmax = (xmax > let_A[i].x) ? xmax : let_A[i].x;
        ymin = (ymin < let_A[i].y) ? ymin : let_A[i].y;
        ymax = (ymax > let_A[i].y) ? ymax : let_A[i].y;
    } // for
    width = xmax - xmin + 1;
    height = ymax - ymin + 1;
    box_x = xmin;
    box_y = ymin;

    // Set offsets to display polygon in centre of screen.
    xoff = (MODE_INFO.xres-width)/2;
    yoff = (MODE_INFO.yres-height)/2;

    // Draw filled polygon.

    // Save the default settings for the source and destination trajectory
    // offset and pitch registers.
    wait_for_idle ();
    save_dst_off_pitch = regr (DST_OFF_PITCH);
    save_src_off_pitch = regr (SRC_OFF_PITCH);

    // Calculate qword offset address of the start of off-screen memory.
    offset = get_xy_offset (0, MODE_INFO.yres) / 8;

    // Calculate the monochrome pitch for the off_screen region where the
    // 1bpp polygon boundry lines will be drawn. Insure that this pitch
    // is an integer multiple of 64 (i.e. round up to the nearest
    // multiple of 64).
    mono_pitch = (unsigned long) ((((MODE_INFO.xres) + 63) / 64) * 64);
    mono_pitch = ((mono_pitch / 8) << 22) & 0xFFC00000;

    // 1. Set destination to 1 bpp for memory clearing and outline drawing.
    wait_for_fifo (16);
    regw (DP_PIX_WIDTH, DST_1BPP);

    // 2. Set scissors to outline drawing region.
    regw (SC_LEFT, 0);
    regw (SC_TOP, 0);
    regw (SC_RIGHT, width - 1);
    regw (SC_BOTTOM, height - 1);

    // 3. Clear off-screen memory where polygon outlines are to be drawn
    // insure engine idleness for GUI register reading.
    wait_for_idle ();

    // Set destination operations to off-screen memory.
    regw (DST_OFF_PITCH, mono_pitch | offset);

    // Clear memory.
    regw (DP_SRC, FRGD_SRC_FRGD_CLR);
    regw (DP_MIX, FRGD_MIX_ZERO | BKGD_MIX_ZERO);
    regw (DST_X, 0);
    regw (DST_Y, 0);
    regw (DST_HEIGHT, height);
    regw (DST_WIDTH, width);

    // 4. Set mix to XOR.
    regw (DP_MIX, FRGD_MIX_D_XOR_S | BKGD_MIX_ZERO);
    regw (DP_FRGD_CLR, 1);

    // 5. Set the DST_POLYGON_ENABLE bit, clear LAST_PEL bit.
    regw (DST_CNTL, DST_POLYGON_ENABLE |
                    DST_Y_TOP_TO_BOTTOM | DST_X_LEFT_TO_RIGHT);

    // 6. Draw all polygon outlines from top to bottom with LAST_PEL_OFF.
    for (i = 0; i < size_A; i++)
    {
        x1 = let_A[i].x - box_x;
        y1 = let_A[i].y - box_y;
        x2 = let_A[i+1].x - box_x;
        y2 = let_A[i+1].y - box_y;

        // Swap points if direction is not top to bottom.

        if (y1 > y2)
        {
            temp = y1;
            y1 = y2;
            y2 = temp;

            temp = x1;
            x1 = x2;
            x2 = temp;
        } // if
        draw_line (x1, y1, x2, y2);
    } // for

    // 7. Set scissors to the final destination area.
    wait_for_fifo (4);
    regw (SC_LEFT, 0);
    regw (SC_TOP, 0);
    regw (SC_RIGHT, MODE_INFO.xres - 1);
    regw (SC_BOTTOM, MODE_INFO.yres - 1);

    // 8. Set all necessary destination context registers.

    // Ensure engine idleness for GUI register reading.
    wait_for_idle ();

    // Set source operations to off-screen memory: Set source trajectory
    // pitch to that of the off-screen polygon boundary region, the offset
    // to the start of this off-screen memory region.
    regw (SRC_OFF_PITCH, mono_pitch | offset);

    // Set destination operations to on-screen memory: Set the destination
    // trajectory pitch to the native pitch, the offset to the start of
    // the display in on-screen memory.
    regw (DST_OFF_PITCH, save_dst_off_pitch & 0xFFC00000);

    // Set destination colour depth to current mode.
    switch (MODE_INFO.bpp)
    {
        case 4:
            colour_depth = DST_4BPP;
            break;

        case 8:
            colour_depth = DST_8BPP;
            break;

        case 15:
            colour_depth = DST_15BPP;
            break;

        case 16:
            colour_depth = DST_16BPP;
            break;

        case 32:
            colour_depth = DST_32BPP;
            break;
    } // switch
    regw (DP_PIX_WIDTH, SRC_1BPP | colour_depth);

    // Set desired mix and colour values.
    regw (DP_MIX, FRGD_MIX_S | BKGD_MIX_D);
    regw (DP_FRGD_CLR, get_colour_code (LIGHTRED));

    // 9. Setup blit source registers to point to polygon outline area.
    wait_for_fifo (8);
    regw (SRC_X, 0);
    regw (SRC_Y, 0);
    regw (SRC_HEIGHT1, height);
    regw (SRC_WIDTH1, width);

    // 10. Setup blit destination registers to point to final destination area.
    regw (DST_X, box_x+xoff);
    regw (DST_Y, box_y+yoff);
    regw (DST_HEIGHT, height);

    // 11. Blit.
    regw (DST_WIDTH, width);

    // Restore DST_OFF_PITCH and SRC_OFF_PITCH before drawing the polygon
    // outline.
    wait_for_fifo (2);
    regw (DST_OFF_PITCH, save_dst_off_pitch);
    regw (SRC_OFF_PITCH, save_src_off_pitch);

    // Draw a WHITE border around shape using regular lines
    //
    //    This polygon bit in DST_CNTL must be turned off for regular line
    //    drawing.
    wait_for_fifo (1);
    regw (DST_CNTL, DST_Y_TOP_TO_BOTTOM | DST_X_LEFT_TO_RIGHT);
    regw (DP_FRGD_CLR, get_colour_code (WHITE));
    for (i = 0; i < size_A; i++)
    {
        x1 = let_A[i].x;
        y1 = let_A[i].y;
        x2 = let_A[i+1].x;
        y2 = let_A[i+1].y;

        // Swap let_A if direction is not top to bottom.

        if (y1 > y2)
        {
            temp = y1;
            y1 = y2;
            y2 = temp;

            temp = x1;
            x1 = x2;
            x2 = temp;
        } // if
        draw_line (x1 + xoff, y1 + yoff, x2 + xoff, y2 + yoff);
    } // for

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

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

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