/******************************************************************************
 * Bus Mastering sample code (using GUI engine for rectangle draws)           *
 *                                                                            *
 *                                                                            *
 * 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"

#define DESCRIPTOR_LIST_SIZE 24
#define NUM_RECTS 170

void main (int argc, char *argv[])
{
    unsigned long temp = 0;
    unsigned long temp2 = 0;
    unsigned long psize = 0;
    unsigned long baseaddress;
    unsigned long segment = 0;
    unsigned long segment2 = 0;

    unsigned long selector = 0;
    unsigned long selector2 = 0;
    unsigned long colour = 0;

    
    char *data = NULL;
    char *buffer = NULL;
    char *descriptor = NULL;
    
    unsigned long register_addrdata[6] =
    {
    DP_FRGD_CLR /4,
    0,
    DST_Y_X /4,
    0,
    DST_HEIGHT_WIDTH /4,
    0
    };
    
    int a, b, height, width, dstx, dsty, c1, c2, c3;
    BM_LIST_DESCRIPTOR bm;
    FILE *result;
    
    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);
    }

    // this program does not work in 24 bpp
    if (MODE_INFO.bpp == 24)
    {
        finish ();
        printf ("\nThis program does not operate in 24bpp.\n");
        exit (1);
    }

    // determine if bus mastering is possible
    // i.e. is a RAGE PRO installed
    if(!is_pro())
    {
        finish ();
        printf ("\nThis program does not support this device.");
        printf ("\nA 3D RAGE PRO based graphics adapter must be installed!\n");
        exit (1);
    }


    // clear the screen 
    clear_screen (0, 0, MODE_INFO.xres, MODE_INFO.yres);

    // allocate 32k, so we can align it on 16k boundary
    // it must be 16k aligned, as this is the size of our 
    // CIRCULAR_BUF_SIZE@BM_GUI_TABLE
    psize = 2048; // allocate 32k (2048 paragraphs), so we can 16k align it.
    
    if (DPMI_allocdosmem( psize, &segment, &selector) == 0)
    {
        // can't allocate memory for image, shut down
        finish ();
        printf("Unable to allocate system memory!\n");
        exit (1);
    }

    // allocate a second buffer for the descriptor.
    psize = 2; // allocate 32 bytes
    
    if (DPMI_allocdosmem( psize, &segment2, &selector2) == 0)
    {
        // can't allocate DOS memory 
        finish ();
        printf("Unable to allocate system memory!\n");
        exit (1);
    }
    // this is the area for the descriptor
    baseaddress = segment2 << 4;
    descriptor = (char *)baseaddress;
    

    // determine the physical memory location
    baseaddress = segment << 4;
    // 16k align the baseaddress
    baseaddress += (baseaddress%16384); 
    

    // this creates a pointer to where our data (sequence of register writes
    // and data) will reside
    data = (char *)baseaddress; // a pointer to where the data will be stored
    buffer = data; // a "dummy" variable that will be incremented
    
    // let's enable block 1 (MM regs), as bus master regs are there
    // Also, & ~(1 << 6) enables bus mastering.
    wait_for_idle ();
    temp = regr (BUS_CNTL);
    temp = temp | 0x08000000 & ~(1 << 6); // enable MM reg block and bm
    regw (BUS_CNTL, temp);

    wait_for_idle ();

    while (!kbhit())
    {
        // This loop creates the data for the descriptor. 
        // The list is created by alternating dwords; the address of 
        // the register you wish to write (in it's MM offset format), 
        // followed by the value you wish to write to that register.
        // You can have up to 4096 bytes per list descriptor entry.

        // In this example, we are using the engine to draw a simple 
        // rectangle with a solid fill.  
        // For this we require 3 register writes:
        // DP_FRGD_CLR, DST_Y_X, DST_HEIGHT_WIDTH
        
        for (a=0;a<NUM_RECTS;a++)
        {
            
            switch (MODE_INFO.bpp)
            {
                case 8:     colour = (unsigned long)rand()%256;
                            break;
                
                case 15:    c1 = rand()%32;
                            c2 = rand()%32;
                            c3 = rand()%32;
                            colour = (c1 << 10) | (c2 << 5) | (c3);
                            break;
                
                case 16:    c1 = rand()%32;
                            c2 = rand()%64;
                            c3 = rand()%32;
                            colour = (c1 << 11) | (c2 << 5) | (c3);
                            break;
                
                case 32:    c1 = rand()%256;
                            c2 = rand()%256;
                            c3 = rand()%256;
                            colour = (c1 << 16) | (c2 << 8) | (c3);
                            break;
                default:    break;
            }

            register_addrdata[1] = colour;
            
            dsty = rand()%(MODE_INFO.yres);
            if (dsty == 0) dsty = 2;
            dstx = rand()%(MODE_INFO.xres);
            if (dstx == 0) dstx = 2;
            
            register_addrdata[3] = (unsigned long)dsty | ((unsigned long)dstx << 16);
            
            height = rand()%(MODE_INFO.yres - dsty);
            if (height ==0) height = 2;
            
            width = rand()%(MODE_INFO.xres - dstx);
            if (width == 0) width = 2;
            
            register_addrdata[5] = (unsigned long)height | ((unsigned long)width << 16);

            // write the data to our buffer
            memcpy ( buffer, register_addrdata, DESCRIPTOR_LIST_SIZE);
            buffer += DESCRIPTOR_LIST_SIZE;

        }

        // calculate the bus master list descriptor entry:
        bm.FRAME_BUF_OFFSET = BM_ADDR + MODE_INFO.linear_memreg_offset;
        bm.SYSTEM_MEM_ADDR = (unsigned long)data;
        bm.COMMAND = (NUM_RECTS * DESCRIPTOR_LIST_SIZE) | 0x40000000 | 0x80000000;
        bm.RESERVED = 0; // set it to 0 to avoid garbage

        // write the data to the descriptor buffer
        memcpy (descriptor, &bm, sizeof(BM_LIST_DESCRIPTOR));
        
        wait_for_idle ();
        
        // send the address of the list descriptor entry to BM_GUI_TABLE
        regw (BM_GUI_TABLE, (unsigned long)descriptor | BM_CIR_BUF_SIZE_16K);

        // enable GUI bus mastering, and sync the bus master to the GUI
        regw (SRC_CNTL, SRC_BM_ENABLE | SRC_BM_SYNC | (3 << 10) );

        // initiate an engine operation
        regw (DST_HEIGHT_WIDTH, 000); // this value doesn't matter

        // restore previous SRC_CNTL to disable busmastering
        wait_for_fifo (1);
        regw (SRC_CNTL, 0);
        
        buffer = data; // reset the pointer to the buffer
        
    } // end of while


    wait_for_idle ();

    // wait for a key to be pressed.
    getch ();
    
    // disable the multimedia register block
    regw (BUS_CNTL, (regr (BUS_CNTL)) & 0xF7FFFFFF );
    
    finish ();
    
    // free the dosmem
    if (DPMI_freedosmem (selector) == 0)
    {
        printf("\nCould not deallocate memory!\n");
    }

    if (DPMI_freedosmem (selector2) == 0)
    {
        printf("\nCould not deallocate memory!\n");
    }

    exit (0);                           // No errors.

} // main


