/****************************************************************************
 * Rage 128 Bus Mastering sample code                                       *
 *                                                                          *
 * Copyright (c) 1999 ATI Technologies Inc.  All rights reserved.           *
 ****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <i86.h>
#include "..\util\defines.h"
#include "..\util\regdef.h"
#include "..\util\main.h"

_bm_info BM_INFO;


/****************************************************************************
 * Main Program to demonstrate system bus mastering on the Rage 128         *
 *  Function: A test image is bus mastered from system memory to display    *
 *            memory                                                        *
 *    Inputs: Arguments for mode spatial and colour resolution              *
 *   Outputs: NONE                                                          *
 ****************************************************************************/
void main (int argc, char *argv[])
{
    DWORD temp = 0;
    DWORD saved_bus_cntl = 0;
    DWORD fboffset = 0;
    DWORD psize = 0;
    DWORD baseaddress = 0;
    DWORD segment = 0;
    DWORD selector = 0;
    DWORD savebmchunk0, savebmchunk1;
    WORD posx, posy;
    char *data = NULL;
    char *descriptor_data = NULL;
    int width, height, linebytelength;
    int num_descriptors, bytes_left, a;
    _img_info IMG_INFO;
    BM_LIST_DESCRIPTOR bm;

    /* First, run StartUp function to set up the application */
    R128_StartUp (argc, argv);

    R128_ClearScreen (LIGHTMAGENTA);
    R128_DrawRectangle (R128_AdapterInfo.xres/3, R128_AdapterInfo.yres/3,
                        R128_AdapterInfo.xres/3, R128_AdapterInfo.yres/3,
                        BLACK);

    IMG_INFO = R128_GetImageFileHeaderInfo (R128_GetBPPValue (R128_AdapterInfo.bpp));

    if (IMG_INFO.width == NULL)
    {
        // Could not load image, shut down
        R128_ShutDown ();
        printf ("\nCould not load image file.");
        printf ("\nTerminating program.");
        exit (1);
    }

    width = IMG_INFO.width;
    linebytelength = width * IMG_INFO.bytepp;
    height = IMG_INFO.height;

    bytes_left = IMG_INFO.size; /* used as a variable to decrement the total bytes left */

    /* calculate how many descriptors we will need */
    num_descriptors = IMG_INFO.size/linebytelength;
    if(IMG_INFO.size % linebytelength) num_descriptors++;

    /* calculate how many paragraphs we will need to allocate */
    psize = (DWORD)IMG_INFO.size/16 + (DWORD)num_descriptors;

    if (DPMI_allocdosmem( psize, &segment, &selector) == 0)
    {
        /* can't allocate memory for image, shut down */
        R128_ShutDown ();
        printf ("\nUnable to allocate system memory for image!");
        exit (1);
    }

    /* create pointer to this memory */
    baseaddress = segment << 4;
    data = (char *)baseaddress;
    IMG_INFO.location = data;

    /* zero all the memory */
    memset (data, 0, (psize*16) );

    /* The descriptors will be located right after the image file. */
    /* Create a pointer to this location. */
    descriptor_data = (char *)(baseaddress + IMG_INFO.size);

    /* open image file and load it into system memory */
    R128_LoadImageIntoSystemMemory (IMG_INFO);
    R128_Delay (10);

    /* Enable bus mastering */
    temp = regr (BUS_CNTL);
    saved_bus_cntl = temp;
    /* Clear BUS_MASTER_DIS @ BUS_CNTL */
    temp &= ~(0x00000040);
    regw (BUS_CNTL, temp);

    /* Enable the BM EOL IRQ */
    temp = regr (GEN_INT_CNTL);
    /* Set BUS_MASTER_EOL_EN @ GEN_INT_CNTL */
    temp |= 0x00010000;
    regw (GEN_INT_CNTL, temp);

    /* Clear the interrupt acknowledge */
    temp = regr (GEN_INT_STATUS);
    temp |= 0x00010000;
    regw (GEN_INT_STATUS, temp);

    savebmchunk0 = regr (BM_CHUNK_0_VAL);
    savebmchunk1 = regr (BM_CHUNK_1_VAL);

    regw (BM_CHUNK_0_VAL, 0x000000FF | BM_GLOBAL_FORCE_TO_PCI);
    /*regw (BM_CHUNK_0_VAL, 0x000000FF); */
    regw (BM_CHUNK_1_VAL, 0x0F0F0F0F);

    /* Get the timeout count for a BM timeout */
    BM_INFO.count = R128_BMGetTimeoutCount (40);
    BM_INFO.entries = R128_BMGetQueueEntries (BM_VIP0_BUF);

    R128_FlushPixelCache ();
    getch ();

    while (!kbhit())
    /*while (getch ()!=32) */
    {
        fboffset = 0;

        posx = rand()%(R128_AdapterInfo.xres - width);
        posy = rand()%(R128_AdapterInfo.yres - height);
        fboffset = (posy * R128_AdapterInfo.xres + posx) * R128_AdapterInfo.bytepp;

        /* create descriptor list */
        for (a = 0; a < num_descriptors; a++)
        {
            bm.FRAME_BUF_OFFSET = fboffset +
                      (DWORD)a * R128_AdapterInfo.xres *
                      R128_AdapterInfo.bytepp;

            bm.SYSTEM_MEM_ADDR = baseaddress + (DWORD)a * linebytelength;

            /* calculate size of transfer for this descriptor */
            if (bytes_left <= linebytelength)
            {
                bm.COMMAND = (DWORD)bytes_left | BM_FORCE_TO_PCI | BM_END_OF_LIST;
            }
            else
            {
                /* we have more than linebytelength remaining, so we can set the */
                /* amount of bytes to transfer to linebytelength */
                bm.COMMAND = (DWORD)linebytelength | BM_FORCE_TO_PCI ;
            }

            bm.RESERVED = 0; /* set it to 0 to avoid garbage */
            memcpy( descriptor_data, &bm, sizeof(BM_LIST_DESCRIPTOR));
            descriptor_data += 16; /* increment pointer for next copy */
            bytes_left = IMG_INFO.size - ((a+1) * linebytelength);

        } /* end of for (a... */

        /* okay, descriptors are set up. */
        /* we need to wait for engine idle before we initiate the blt */
        R128_WaitForIdle ();

        temp = baseaddress + IMG_INFO.size;
        regw (BM_VIP0_BUF, temp | BM_TRIG_SYS_TO_FRAME_IMM); /* this initiates the bm transfer! */

        /* wait for BM_EOL interrupt bit to be set. */
        do
        {
            temp = regr (GEN_INT_STATUS);
            temp &= 0x00010000;
        } while (temp == 0);

        /* acknowledge the interrupt by clearing the bit */
        temp = regr (GEN_INT_STATUS);
        temp |= 0x00010000;
        regw (GEN_INT_STATUS, temp);

        /* reset the desciptor_data pointer */
        descriptor_data = (char *)(baseaddress + IMG_INFO.size);

        /* reset the bytes_left variable */
        bytes_left = IMG_INFO.size;

    } /* end of while(!kbhit.. */

    getch ();

    regw (BM_CHUNK_0_VAL, savebmchunk0);
    regw (BM_CHUNK_1_VAL, savebmchunk1);

    /* make sure the engine is idle before exiting */
    R128_WaitForIdle ();

    /* wait for key to be pressed */
    /* getch (); */

    /* Restore registers */
    regw (BUS_CNTL, saved_bus_cntl);

    /* shut down */
    R128_ShutDown ();

    // free image system memory
    free (IMG_INFO.location);

    /* free the dosmem */
    if (DPMI_freedosmem (selector) == 0)
    {
        printf ("\nCould not deallocate memory!\n");
    }

    exit (0);                           /* No errors. */

} /* main */


