/******************************************************************************
 * Bus Mastering sample code                                                  *
 *                                                                            *
 *                                                                            *
 * Copyright (c) 1994-1998 ATI Technologies Inc.  All rights reserved.        *
 ******************************************************************************/

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

void main (int argc, char *argv[])
{
    unsigned long temp = 0;
    unsigned long saved_crtc_cntl = 0;
    unsigned long saved_bus_cntl = 0;
    unsigned long fboffset = 0;
    unsigned long psize = 0;
    unsigned long baseaddress = 0;
    unsigned long segment = 0;
    unsigned long selector = 0;
    
    char *data = NULL;
    char header_info[32];

    img_handle *ReturnPtr;
    char *descriptor_data = NULL;
    int width, height, size, linebytelength;
    int xindent, yindent, bytes_per_pixel;
    int num_descriptors, bytes_left, a, b;

    FILE *handle;

    BM_LIST_DESCRIPTOR bm;

    printf ("ATI 3D RAGE PRO bus master sample program\n");
    
    if(!(start (argc, argv)))
    {
        printf ("\nPress any key to exit...\n");
        getch ();
        finish ();
        exit (1);
    }

    if (MODE_INFO.bpp != 16)
    {
        finish ();
        printf ("\nBMASTER requires a colour depth of 16 bpp.");
        exit (1);
    }
    
    // determine if bus mastering is possible
    // i.e. is a RAGE PRO installed
    if (!is_pro())
    {
        finish ();
        printf ("\nThis program cannot run on this device.");
        printf ("\nA 3D RAGE PRO based graphics adapter is required.\n");
        exit (1);
    }

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

    // open the test file to get size information
    ReturnPtr = get_img_header("..\\image\\test16.img");

    if (ReturnPtr == NULL)
    {
        // disable accelerated mode and shut down
        finish ();
        printf("Unable to read image header!\n");
        exit (1);
    }

    // store some variables used later
    width = ReturnPtr->image->width;
    linebytelength = width * ReturnPtr->image->bytes_per_pixel;
    height = ReturnPtr->image->height;

    // calculate the size of the file
    size = width * height * ReturnPtr->image->bytes_per_pixel; 
    bytes_left = size; // used as a variable to decrement the total bytes left

    // calculate how many descriptors we will need
    num_descriptors = size/linebytelength;
    if(size%linebytelength) num_descriptors++;
    
    // calculate how many paragraphs we will need to allocate
    psize = (unsigned long)size/16 + (unsigned long)num_descriptors;
    
    if (DPMI_allocdosmem( psize, &segment, &selector) == 0)
    {
        // can't allocate memory for image, shut down
        finish ();
        printf("Unable to allocate system memory for image!\n");
        exit (1);
    }

    // create pointer to this memory
    baseaddress = segment << 4;
    data = (char *)baseaddress;
    
    // zero all the memory
    memset (data, 0, (int)(psize*16) );

    descriptor_data = (char *)(baseaddress + size);
                         
    // open image file and load it into system memory
    handle = fopen( "..\\image\\test16.img", "rb");

    if (handle == NULL)
    {
        // can't open file, shut down
        finish ();
        printf("Unable to open img file!\n");
        exit (1);
    }

    if (fread(header_info, 1, 32, handle) == NULL)
    {
        // can't read file 
        finish ();
        printf("Unable to read img file header data!\n");
        exit (1);
    }

    if (fread(data, 1, size, handle) == NULL)
    {
        // can't read file 
        finish ();
        printf("Unable to read img file!\n");
        exit (1);
        
    }

    fclose (handle);
    
    free (ReturnPtr);
    
    // let's enable block 1 (MM regs), as bus master regs are there
    // Also, & ~(1 << 6) enables bus mastering
    wait_for_fifo (5);
    temp = regr (BUS_CNTL);
    saved_bus_cntl = temp;
    temp = temp | 0x08000000 & ~(1 << 6); // enable mm regs, and bus mastering
    regw (BUS_CNTL, temp);
    
    // enable bus master interrupt
    temp = regr (CRTC_INT_CNTL);
    saved_crtc_cntl = temp;
    regw (CRTC_INT_CNTL, saved_crtc_cntl & 0xFEFFFFFF); // clear the interrupt
    regw (CRTC_INT_CNTL, saved_crtc_cntl | 0x02000000); // enable the interrupt
    
    
    // determine how much we will indent each bus master transfer, so we
    // can visually see the different images after they are drawn
    bytes_per_pixel = MODE_INFO.bpp/8;
    if (MODE_INFO.bpp%8) bytes_per_pixel++;
    xindent = ((MODE_INFO.xres - width) / 9) * bytes_per_pixel;
    yindent = ((MODE_INFO.yres - height) / 9) * MODE_INFO.xres * bytes_per_pixel;
    

    // this is the main loop that repeats the bus master transfer
    for (b=0;b<10;b++)
    {

        // create descriptor list
        for (a = 0; a < num_descriptors; a++)
        {
            bm.FRAME_BUF_OFFSET = fboffset + 
                    (unsigned long)a* MODE_INFO.pitch * MODE_INFO.bpp/8;
            bm.SYSTEM_MEM_ADDR = baseaddress + (unsigned long)a*linebytelength;
            
            // calculate size of transfer for this descriptor
            if (bytes_left <= linebytelength)
            {
                bm.COMMAND = (unsigned long)bytes_left | LAST_DESCRIPTOR;
            }
            else
            {
                // we have more than linebytelength remaining, so we can set the
                // amount of bytes to transfer to linebytelength
                bm.COMMAND = linebytelength;
            }

            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 = 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
        wait_for_idle ();

        temp = baseaddress + size | SYSTEM_TO_FRAME_BUFFER;
        regw (BM_SYSTEM_TABLE, temp); // this initiates the bm transfer!
        
        // wait for the end of the transfer by polling 
        // BUSMASTER_EOL_INT@CRTC_INT_CNTL
        do
        {
            temp = regr (CRTC_INT_CNTL);
            temp &= 0x02000000;
        }
        while (temp == 0);

        // now that the interrupt has occured, we must clear it.
        regw (CRTC_INT_CNTL, saved_crtc_cntl | 0x02000000); // clear the interrupt

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

        // reset the bytes_left variable
        bytes_left = size; 

        // indent the next image
        fboffset += yindent + xindent; 
    
    } // end of for b...

    // make sure the engine is idle before exiting
    wait_for_idle ();
    
    // wait for key to be pressed
    getch ();

    // Restore registers
    regw (BUS_CNTL, saved_bus_cntl);
    regw (CRTC_INT_CNTL, saved_crtc_cntl);
    
    // shut down
    finish ();
    
    // free the dosmem
    if (DPMI_freedosmem (selector) == 0)
    {
        printf ("\nCould not deallocate memory!\n");
    }

    exit (0);                           // No errors.

} // main


