/******************************************************************************
 * mach64 Chapter 6 sample code                                               *
 *                                                                            *
 * meblit.c - This program uses the mach64 engine to perform a monochrome     *
 * expansion bitblit.  (using strictly linear source)                         *
 *                                                                            *
 * 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.
unsigned long reversedata (unsigned long data, int nbits);
unsigned long convertpattern (unsigned long pattern);

/******************************************************************************
 * Main Program to demonstrate a strictly linear source bitblit               *
 *  Function: Draws a character 'a' in linear memory on screen.  This         *
 *            pattern is then blitted 10 times on screen using monochrome     *
 *            expansion.                                                      *
 *    Inputs: Arguments for mode spatial and colour resolution                *
 *   Outputs: NONE                                                            *
 ******************************************************************************/

void main (int argc, char *argv[])
{
    int srcx;                           // Top left x co-ord of source area.
    int srcy;                           // Top left y co-ord of source area.
    int srcwidth;                       // Width of source area.
    int srcheight;                      // Height of source area.
    int dstx;                           // Top left x co-ord of dest area.
    int dsty;                           // Top left y co-ord of dest area.
    int dstwidth;                       // Width of destination area.
    int dstheight;                      // Height of destination area.
    int loop, hostwrts;
    unsigned long colour_depth;
    unsigned long offset;
    unsigned long bitmap[13];

    printf ("mach64 Chapter 6 sample code\n"
            "\n"
            "meblit.c\n"
            "This program demonstrates a monochrome expansion bitblit.  First a\n"
            "bitmap letter 'a' is written to on screen memory so it is visible.\n"
            "This information is used as the source for the monochrome expansion.\n"
            "it is blitted 10 times using the colours White and Light Blue.\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 - Monochrome expansions are not directly
    // supported in 24 bpp modes.
    if (MODE_INFO.bpp == 24)
    {
        // Disable accelerator mode and switch back to VGA text mode.
        finish ();
        printf ("Monochrome expansions are not directly supported in 24 bpp "
                "modes.\n");
        exit (1);
    } // if

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

    // Fill the bitmap to be used as monochrome source data - the letter 'a'.
    bitmap[0]  = 0x0003FFFF;            // 00000000000000111111111111111111
    bitmap[1]  = 0x000C0006;            // 00000000000011000000000000000110
    bitmap[2]  = 0xF0300018;            // 11110000001100000000000000011000
    bitmap[3]  = 0x38C3F860;            // 00111000110000111111100001100000
    bitmap[4]  = 0x3318398E;            // 00110011000110000011100110000111
    bitmap[5]  = 0x0C780630;            // 00001100011110000000011000110000
    bitmap[6]  = 0x319F18FC;            // 00110001100111110001100011111100
    bitmap[7]  = 0xC60F630F;            // 11000110000011110110001100001111
    bitmap[8]  = 0x3E0D8C06;            // 00110111000011011000110000000110
    bitmap[9]  = 0xC7E6FFFB;            // 11000111111001101111111111111011
    bitmap[10] = 0xFFF8000D;            // 11111111111110000000000000001101
    bitmap[11] = 0x0000001F;            // 00000000000000000000000000011111
    bitmap[12] = 0x00000000;            // 00000000000000000000000000000000

    //  11111111111111111  11111111111111111
    //  10000000000000001  10000000000000001
    //  10000000000000001  10000000000000001
    //  10000000000000001  10000000000000001
    //  10000000000000001  10000000000000001
    //  10000011110000001  10000001111000001
    //  10000111111100001  10000111111100001
    //  10000111001110001  10001110011100001
    //  10001100000111001  10011100000110001
    //  10001100000011001  10011000000110001
    //  10001111000000001  10000000011110001
    //  10001111110000001  10000001111110001
    //  10001100111110001  10001111100110001
    //  10001100001111001  10011110000110001
    //  10001100000111101  10111100000110001
    //  10001100000001101  10110000000110001
    //  10011011100001101  10110000111011001
    //  10111111111111101  10111111111111101
    //  10111000111111001  10011111100011101
    //  10000000000000001  10000000000000001
    //  11111111111111111  11111111111111111
    //  00000000000000000  00000000000000000
    //  00000000000000000  00000000000000000
    //  00000000000000000  00000000000000000
    //  00000000                    00000000

    // Setup source and destination coordinates and sizes:
    //    Note that these coordinates are in the modal bpp. An odd sized
    //    bitmap has been chosen to show how to handle remainder pixels (a
    //    total bit size that doesn't divide evenly into 32). The bitmap
    //    must be loaded into video memory via the HOST register. Each
    //    write transfers 32 pixels.
    dstx = 0;                           // Resultant co-ord for blit expansion.
    dsty = 10;
    dstwidth = 17;                      // Size of monochrome bitmap.
    dstheight = 21;

    srcx = 0;                           // Location of mono blit source data.
    srcy = 0;
    srcwidth = dstwidth * dstheight;    // linear size of mono source data.
    srcheight = 1;

    // Determine qword offset of coordinate (srcx, srcy) - this is the format
    // required for the DST_OFF_PITCH and SRC_OFF_PITCH registers.
    offset = get_xy_offset (srcx, srcy) / 8;

    // Determine number of host writes needed. Each host write produces 32
    // pixels (in monochrome). If the number of host writes is not even,
    // the remainder in the last host write will be thrown away by the engine.
    hostwrts = srcwidth / 32;
    if (srcwidth != (hostwrts * 32))
    {
        // Round up to handle pixel remainder.
        hostwrts++;
    } // if

    // Setup a monochrome source pattern.

    // This is done by drawing host data linearly in memory. The pattern will
    // be placed at (srcx, srcy) so that it is visible on the screen. The
    // linear source data will be monochrome expanded into a DSTWIDTH x
    // DSTHEIGHT rectangle (dstwidth * dstheight bits). A "1" bit in the
    // source data will be expanded to the foreground colour and a "0" will be
    // expanded to the background colour. The resultant pattern will be an 'a'
    // with a border.

    // Set engine host, source, and destination to 1 bpp; also set the
    // byte order bit which affects 1 and 4 bpp modes.

    wait_for_fifo (2);
    if (MODE_INFO.bpp == 4)
    {
        // For 4 bpp modes, the CRTC byte format is reversed.
        regw (DP_PIX_WIDTH, BYTE_ORDER_MSB_TO_LSB |
                            HOST_1BPP | SRC_1BPP | DST_1BPP);
    }
    else
    {
        regw (DP_PIX_WIDTH, BYTE_ORDER_LSB_TO_MSB |
                            HOST_1BPP | SRC_1BPP | DST_1BPP);
    } // if

    // Set engine to use monochrome host registers for source data:
    //
    //      - for monochrome host data of "1", the foreground source channel
    //        is used
    //      - for monochrome host data of "0", the background source channel
    //        is used
    regw (DP_SRC, MONO_SRC_HOST | FRGD_SRC_FRGD_CLR | BKGD_SRC_BKGD_CLR);

    // Set foreground mix to ONE and background mix to ZERO.
    regw (DP_MIX, FRGD_MIX_ONE | BKGD_MIX_ZERO);

    // Setup monochrome host data to be copied to screen memory at
    // (srcx, srcy):
    //
    //      To avoid converting 8 bpp coordinates into 1 bpp coordinates and
    //      expanding the engine scissors, the 'offset' value calculated
    //      above will allow drawing at (srcx, srcy) by setting the
    //      coordinate registers DST_X, DST_Y to (0, 0)

    // Add offset without altering the pitch.
    wait_for_fifo (5);
    regw (DST_OFF_PITCH, (regr (DST_OFF_PITCH) & 0xFFC00000) | offset);

    regw (DST_X, 0);
    regw (DST_Y, 0);
    regw (DST_HEIGHT, 1);
    regw (DST_WIDTH, srcwidth);

    // Copy host data to screen memory - pattern order is LSB to MSB.
    for (loop = 0; loop < hostwrts; loop++)
    {
        wait_for_fifo (1);
        regw (HOST_DATA0, convertpattern (bitmap[loop]));
    } // for

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

    // Restore destination offset to zero.
    regw (DST_OFF_PITCH, regr (DST_OFF_PITCH) & 0xFFC00000);

    // Perform blit expansion from the monochrome source data.

    // Determine modal colour depth for destination.
    switch (MODE_INFO.bpp)
    {
        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

    // Set engine source colour depth to 1 bpp and destination colour depth
    // to the modal colour depth.
    regw (DP_PIX_WIDTH, BYTE_ORDER_LSB_TO_MSB |
                        HOST_1BPP | SRC_1BPP | colour_depth);

    // Set engine to use blit registers for monochrome source data:
    //
    //      - for monochrome blit of "1", the foreground colour register
    //        is used
    //      - for monochrome blit of "0", the background colour register
    //        is used
    regw (DP_SRC, MONO_SRC_BLIT | FRGD_SRC_FRGD_CLR | BKGD_SRC_BKGD_CLR);

    // Set foreground and background mix to OVERPAINT.
    regw (DP_MIX, FRGD_MIX_S | BKGD_MIX_S);

    // Foreground colour = WHITE.
    regw (DP_FRGD_CLR, get_colour_code (WHITE));

    // Background colour = LIGHTBLUE.
    regw (DP_BKGD_CLR, get_colour_code (LIGHTBLUE));

    // Add offset without altering the pitch.
    wait_for_fifo (11);
    regw (SRC_OFF_PITCH, (regr (SRC_OFF_PITCH) & 0xFFC00000) | offset);

    // Set source attribute to linear source data format.
    regw (SRC_CNTL, SRC_LINEAR_ENABLE);

    // Set source registers to monochrome data location.
    regw (SRC_X, 0);
    regw (SRC_Y, 0);
    regw (SRC_HEIGHT1, 1);
    regw (SRC_WIDTH1, srcwidth);

    // Set destination draw direction and enable engine tiling.
    regw (DST_CNTL, DST_Y_TOP_TO_BOTTOM | DST_X_LEFT_TO_RIGHT |
                    DST_X_TILE | DST_Y_TILE);

    // Set destination start coordinates and rectangle size to fill.
    regw (DST_X, dstx);
    regw (DST_Y, dsty);
    regw (DST_HEIGHT, dstheight);

    // Since the X & Y tiling is enabled in DST_CNTL, DST_X and DST_Y will
    // be automatically updated by the engine after each operation.

    for (loop = 0; loop < 10; loop++)
    {
        wait_for_fifo (1);
        regw (DST_WIDTH, dstwidth);     // Do monochrome expansion blit.
    } // for

    // Restore source offset to zero.
    wait_for_fifo (1);
    regw (SRC_OFF_PITCH, regr (SRC_OFF_PITCH) & 0xFFC00000);

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

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

    exit (0);                           // No errors.

} // main


/******************************************************************************
 * reversedata                                                                *
 *  Function: reverses the bits of the given data.                            *
 *    Inputs: data - data to be reversed                                      *
 *            nbits - number of bits in the data                              *
 *   Outputs: newdata - resultant data                                        *
 *     Notes: this is a general utility                                       *
 ******************************************************************************/

unsigned long reversedata (unsigned long data, int nbits)
{
    unsigned long newdata, bit, mask, shifter;
    int i;

    if (nbits > 32) nbits = 32;

    newdata = 0;
    shifter = 0;
    mask = 1;
    for (i = 0; i < nbits; i++)
    {
        bit = (data & mask) >> shifter;
        newdata = (newdata << 1) | bit;

        shifter++;
        mask = mask << 1;
    } // for

    return (newdata);

} // reversedata


/******************************************************************************
 * convertpattern                                                             *
 *  Function: rearranges fill pattern for 4 bpp modes                         *
 *    Inputs: pattern - pattern to be converted                               *
 *   Outputs: convert - converted pattern                                     *
 *     Notes: Since the default CRTC pixel order is MSB to LSB in each        *
 *            byte for 4bpp modes, fill patterns need converting.             *
 ******************************************************************************/

unsigned long convertpattern (unsigned long pattern)
{
    unsigned long byte1, byte2, byte3, byte4;

    // Don't convert if not 4 bpp mode.
    if (MODE_INFO.bpp != 4)
    {
        return (pattern);
    } // if

    byte1 = reversedata (pattern & 0xFF, 8);
    byte2 = reversedata ((pattern & 0xFF00) >> 8, 8) << 8;
    byte3 = reversedata ((pattern & 0xFF0000) >> 16, 8) << 16;
    byte4 = reversedata ((pattern & 0xFF000000) >> 24, 8) << 24;

    return (byte4 | byte3 | byte2 | byte1);

} // convertpattern
