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


// Keyboard scan and ascii codes for demo.

#define ESC         0x1B    // ascii code
#define LEFT_ARROW  0x4B    // scan code
#define RIGHT_ARROW 0x4D    // scan code
#define UP_ARROW    0x48    // scan code
#define DOWN_ARROW  0x50    // scan code
#define HOME        0x47    // scan code
#define END         0x4F    // scan code


// Prototypes.

int get_key (void);

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

void main (int argc, char *argv[])
{
    unsigned long offset;
    int colour, ch;
    int xindex, yindex, xmax, ymax;
    int max_x, max_y, size_x, size_y;

    printf ("mach64 Chapter 7 sample code\n"
            "\n"
            "pan.c\n"
            "This program demonstrates a panning in a virtual desktop.  Use the\n"
            "cursor arrows to move around the screen.  The home and end keys will\n"
            "position the screen to the top-left corner and bottom-right corner\n"
            "respectively.  The ESC key will exit.\n"
            "\n"
            "Pan is only available in 640 x 480, 8bpp mode.\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.

    // Detect mach64

    if (!init_graphics ())
    {
        printf ("ERROR: Graphics not initialized properly!\n");
        exit (1);
    } // if

    // Fill QUERY_DATA using BIOS calls 08h and 09h and set globals.

    if (!long_query ())
    {
        printf ("ERROR: long query failed!\n");
        exit (1);
    } // if

    // Save old video mode.

    get_old_mode ();

    printf (" Press Any Key To Begin\n");
    getch ();

    process_command_line (argc, argv);
    if ((GMODE_RES != 640) || (GCLR_DEPTH != 8))
    {
        finish ();
        printf ("Pan is only available in 640x480 and 8bpp.\n");
        exit (1);
    } // if

    // Load and set mode and set globals.  '0' indicates 1024 pitch.

    if (!load_and_set_mode (GMODE_RES, GCLR_DEPTH, 0))
    {
        printf ("ERROR: mode setting failed!\n");
        exit (1);
    } // if

    // Enable aperture.
    if (!enable_aperture ())
    {
        finish ();
        printf ("ERROR: enable aperture failed!\n");
        exit (1);
    } // if

    // Setup engine context.
    init_engine ();

    // To scroll and pan the display, the viewable display area must be
    // smaller than the CRTC display area. In this case, the viewable area
    // is 640x480 while the CRTC display area is 1024x1024.

    // Desktop size = 1024 x 1024.

    max_x = 1024;
    max_y = 1024;

    // Viewable screen size = 640 x 480.

    size_x = 640;
    size_y = 480;

    // Adjust engine scissors to desktop area (1024x1024).

    wait_for_fifo (4);
    regw (SC_LEFT, 0);
    regw (SC_TOP, 0);
    regw (SC_RIGHT, max_x - 1);
    regw (SC_BOTTOM, max_y - 1);

    // Clear area.
    clear_screen (0, 0, max_x, max_y);

    // Fill area with some full size rectangles (CRTC width).
    for (colour = DARKBLUE-1; colour < WHITE; colour++)
    {
        regw (DP_FRGD_CLR, (get_colour_code (colour + 1)));
        draw_rectangle (colour * 30, colour * 30,
                        max_x - (colour * 60) - 1, max_y - (colour * 60) - 1);
    } // for

    // Scroll and pan around image:
    //
    //   Viewable display is 640x480
    //   Image size is 1024x1024
    //
    //   ESC         - exit
    //   LEFT_ARROW  - move left
    //   RIGHT_ARROW - move right
    //   UP_ARROW    - move up
    //   DOWN_ARROW  - move down
    //   HOME        - move to top-left corner
    //   END         - move to bottom-right corner

    xindex = 0;
    yindex = 0;

    xmax = (max_x - size_x) / 2;        // 2 pixels per step (in 8 bpp).
    ymax = (max_y - size_y) / 2;        // 2 lines per step.
    if (MODE_INFO.bpp == 4)
    {
        xmax = xmax / 2;
    }
    else
    {
        xmax = xmax * (MODE_INFO.bpp / 8);
    } // if

    offset = 0;
    ch = 0;
    while ((ch & 0xFF) != ESC)
    {
        // Wait for key input.
        while (kbhit () == 0) ;
        ch = get_key ();

        // Position CRTC offset according to key input.
        if ((ch & 0xFF) != ESC)
        {
            switch (ch >> 8)
            {
                case LEFT_ARROW:        // Move left 8 pixels.
                    if (xindex > 0)
                    {
                        offset = offset - 1;
                        xindex = xindex - 4;
                    } // if
                    break;

                case RIGHT_ARROW:       // Move right 8 pixels.
                    if (xindex < xmax)
                    {
                        offset = offset + 1;
                        xindex = xindex + 4;
                    } // if
                    break;

                case UP_ARROW:          // Move up 8 pixels.
                    if (yindex > 0)
                    {
                        if (MODE_INFO.bpp == 4)
                        {
                            offset = offset - 4*(256 / 2);
                        }
                        else
                        {
                            offset = offset - 4*(256 * (MODE_INFO.bpp / 8));
                        } // if
                        yindex = yindex - 4;
                    } // if
                    break;

                case DOWN_ARROW:        // Move down 8 pixels.
                    if (yindex < ymax)
                    {
                        if (MODE_INFO.bpp == 4)
                        {
                            offset = offset + 4*(256 / 2);
                        }
                        else
                        {
                            offset = offset + 4*(256 * (MODE_INFO.bpp / 8));
                        } // if
                        yindex = yindex + 4;
                    } // if
                    break;

                case HOME:              // Move to top-left corner (0, 0).
                    offset = 0;
                    xindex = 0;
                    yindex = 0;
                    break;

                case END:               // Move to bottom-right corner.

                    // Calculate dword address of coordinate
                    // (1024 - 640, 1024 - 480).
                    offset = (unsigned long) (max_y - size_y);

                    // Pitch.
                    offset = (unsigned long) (offset * max_x);
                    if (MODE_INFO.bpp == 4)
                    {
                        offset = (unsigned long) (offset / 2);
                        offset = (unsigned long) (offset +
                                                  ((max_x - size_x) / 2));
                    }
                    else
                    {
                        offset = (unsigned long) (offset * (MODE_INFO.bpp / 8));
                        offset = (unsigned long) (offset +
                                                  ((MODE_INFO.bpp / 8) *
                                                   (max_x - size_x)));
                    } // if
                    offset = offset / 8;
                    xindex = xmax;
                    yindex = ymax;
                    break;
            } // switch

            // Vary CRTC offset while maintaining mode pitch.

            regw (CRTC_OFF_PITCH,
                  (regr (CRTC_OFF_PITCH) & 0xFFC00000) | offset);
        } // if
    } // while

    // Batch command to restore old mode.

    finish ();

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


/******************************************************************************
 * get_key                                                                    *
 *  Function: Get keyboard scan code using system ROM call                    *
 *    Inputs: NONE                                                            *
 *   Outputs: NONE                                                            *
 *     Notes: Upper 8 bits = scan code                                        *
 *            Lower 8 bits = ascii code                                       *
 ******************************************************************************/

int get_key (void)
{
    union REGS regs;
    regs.x.eax = 0x1000;
    int386 (0x16, &regs, &regs);

    return (regs.x.eax);

} // get_key
