/****************************************************************************
 * Project: RAGE 128 / 128 PRO / MOBILITY 128 DDK
 *
 * File: ch3lib.c
 *
 * Purpose: Chapter 3 library function code                               
 *                                                                         
 * Functions:
 *
 *     WORD get_old_mode (void)                                             
 *     WORD set_old_mode (void)                                            
 *     void process_command_line (int argc, char *argv[])                 
 *     DWORD phys_to_virt (DWORD physical, DWORD size)                  
 *     BYTE R128_Detect (void)                                          
 *     BYTE R128_FindRom (void)                                             
 *
 * Date last modified: [MS 02-10-00] 
 *
 * Notes:       This file is provided under non-disclosure agreements.
 *              It is and remains the property of ATI Technologies Inc.
 *              Any use of this file or the information it contains to
 *              develop products commercial or otherwise must be with the
 *              permission of ATI Technologies Inc.
 *                                                                         
 * Copyright (c) 1999-2000 ATI Technologies Inc.  All rights reserved.           
 ****************************************************************************/

/****************************************************************************
 * Change Log
 *
 * MS 02-09-00: R128_Detect updated Device ID listing with R128 4X/128PRO/M3
 * MS 02-09-00: R128_FindRom added M3_sig, Mobility 128 signature
 *
 ****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include "defines.h"
#include "main.h"
#include "regdef.h"

DWORD BPPOverride = 0; // Override 8bpp mode for Chapter 6 3D samples.

void R128_ShutDown (void)
{
    DWORD temp;

    R128_ResetEngine ();

    if ((R128_AdapterInfo.FLAGS & (R128_USE_BIOS)) == 0)
    {
        temp = PLL_regr (PPLL_CNTL);
        temp &= ~(0x00030000);
        PLL_regw (PPLL_CNTL, temp);
    }

    set_old_mode ();
    return;
} // R128_ShutDown

/****************************************************************************
 * get_old_mode                                                             *
 *  Function: saves the old mode information in OLD_MODE                    *
 *    Inputs: NONE                                                          *
 *   Outputs: returns 1 upon completion                                     *
 ****************************************************************************/

WORD get_old_mode (void)
{
    union REGS r;

    memset (&r, 0, sizeof (r));
    r.x.eax = 0x0F00;
    int386 (0x10, &r, &r);
    r.h.ah = 0;

    // Check for 50 line mode.
    if (*((char *) (0x00000484L)) == 0x31)
    {
        r.h.ah = 0x31;
    } // if

    // Check for 43 line mode.
    if (*((char *) (0x00000484L)) == 0x2A)
    {
        r.h.ah = 0x2A;
    } // if
    OLD_MODE = r.x.eax;

    return (1);

} // get_old_mode


/****************************************************************************
 * set_old_mode                                                             *
 *  Function: restores old mode using OLD_MODE                              *
 *    Inputs: NONE                                                          *
 *   Outputs: returns 1 upon completion                                     *
 ****************************************************************************/

WORD set_old_mode (void)
{
    union REGS r;

    r.x.eax = OLD_MODE & 0x00FF;
    int386 (0x10, &r, &r);

    // Check for 43 line mode.
    if ((OLD_MODE & 0xFF00) == 0x2A00)
    {
        r.x.eax = 0x1112;
        r.x.ebx = 0;
        int386 (0x10, &r, &r);
        r.x.eax = 0x1102;
        r.x.ebx = 0;
        int386 (0x10, &r, &r);
    } // if

    // Check for 50 line mode.
    if ((OLD_MODE & 0xFF00) == 0x3100)
    {
        r.x.eax = 0x1112;
        r.x.ebx = 0;
        int386 (0x10, &r, &r);
        r.x.eax = 0x1103;
        r.x.ebx = 0;
        int386 (0x10, &r, &r);
    } // if

    return (1);

} // set_old_mode


/****************************************************************************
 * process_command_line                                                     *
 *  Function: This function sets the default resolution and colour depth    *
 *            settings for each application to 640x480 8bpp and overrides   *
 *            these defaults with the first occurance of valid options in   *
 *            the command line argument list.                               *
 *    Inputs: Arguments for mode spatial and colour resolution              *
 *   Outputs: None                                                          *
 ****************************************************************************/

void process_command_line (int argc, char *argv[])
{
    int i;
    BYTE xres_set = 0;

    // Set default resolution in gloabal variables.

    R128_AdapterInfo.xres = 640;
    R128_AdapterInfo.yres = 480;

        // Set the default colour depth in global variables. Note: Chapter 6 
        // programs may set 16 BPP and 32 BPP as default. 

        if (BPPOverride == 32)
                R128_AdapterInfo.bpp = 32;
        else if (BPPOverride == 16)
                R128_AdapterInfo.bpp = 16;
        else
                R128_AdapterInfo.bpp = 8;

    R128_AdapterInfo.FLAGS = R128_USE_BIOS;

    // Override defaults with valid command line arguments.
    for (i = 1; i < argc; i++)
    {
        // Check for valid x resolution options.
        if (((strcmp (argv[i], "320") == 0) ||
            (strcmp (argv[i], "400") == 0) ||
            (strcmp (argv[i], "512") == 0) ||
            (strcmp (argv[i], "640") == 0) ||
            (strcmp (argv[i], "720") == 0) ||
            (strcmp (argv[i], "800") == 0) ||
            (strcmp (argv[i], "848") == 0) ||
            (strcmp (argv[i], "864") == 0) ||
            (strcmp (argv[i], "1024") == 0) ||
            (strcmp (argv[i], "1152") == 0) ||
            (strcmp (argv[i], "1280") == 0) ||
            (strcmp (argv[i], "1600") == 0) ||
            (strcmp (argv[i], "1920") == 0)) &&
            (xres_set == 0))
        {
            R128_AdapterInfo.xres = (WORD)(atoi (argv[i]));
            xres_set = 1;
        } // if

        // Check for valid y resolution options.
        if (xres_set != 0)
        {
            if ((strcmp (argv[i], "200") == 0) ||
                (strcmp (argv[i], "240") == 0) ||
                (strcmp (argv[i], "300") == 0) ||
                (strcmp (argv[i], "384") == 0) ||
                (strcmp (argv[i], "480") == 0) ||
                (strcmp (argv[i], "600") == 0) ||
                (strcmp (argv[i], "768") == 0) ||
                (strcmp (argv[i], "864") == 0) ||
                (strcmp (argv[i], "1024") == 0) ||
                (strcmp (argv[i], "1200") == 0) ||
                (strcmp (argv[i], "1440") == 0))
            {
                R128_AdapterInfo.yres = (WORD)(atoi (argv[i]));
            } // if

        } // if

        // Check for runtime flags
        if (stricmp (argv[i], "NOBIOS") == 0)
        {
            R128_AdapterInfo.FLAGS &= ~(R128_USE_BIOS);
        }
        else if (stricmp (argv[i], "FORCEPCI") == 0)
        {
            // Force an AGP board to behave like a PCI board (don't use AGP).
            R128_AdapterInfo.FLAGS |= R128_FORCE_PCI;
        }

        // Check for valid colour depth options.
        if ((strcmp (argv[i], "8") == 0) ||
            (strcmp (argv[i], "15") == 0) ||
            (strcmp (argv[i], "16") == 0) ||
            (strcmp (argv[i], "24") == 0) ||
            (strcmp (argv[i], "32") == 0))
                {
            R128_AdapterInfo.bpp = (BYTE)(atoi (argv[i]));
        } // if

    } // for

    return;
} // process_command_line


/****************************************************************************
 * phys_to_virt                                                             *
 *  Function: takes a physical segment address and size and gets the        *
 *            equivalent virtual or logical address which can directly      *
 *            access this memory.  INT 31h function 800h                    *
 *    Inputs: physical - physical address of memory                         *
 *            size - size of address                                        *
 *   Outputs: virtual address that is direct memory accessible              *
 ****************************************************************************/

DWORD phys_to_virt (DWORD physical, DWORD size)
{
    union REGS r;
    struct SREGS sr;
    DWORD retval=0;

    memset (&r, 0, sizeof (r));
    memset (&sr, 0, sizeof (sr));
    r.w.ax = 0x0800;
    r.w.bx = physical >> 16;
    r.w.cx = physical & 0xFFFF;
    r.w.si = size >> 16;
    r.w.di = size & 0xFFFF;
    int386x (0x31, &r, &r, &sr);
    if ((r.w.cflag & INTR_CF) == 0)
    {
        retval = (long) (((long) r.w.bx << 16) | r.w.cx);
    } // if

    return (retval);

} // phys_to_virt


/****************************************************************************
 * R128_PrintInfoStruct                                                     *
 *  Function: Print AdapterInfo Structure                                   *
 *    Inputs: none                                                          *
 *   Outputs: none                                                          *
 ****************************************************************************/

void R128_PrintInfoStruct (void)
{
    printf ("\nAdapterInfo:\n");
    printf ("PCI Vendor ID: %4.4X\n", R128_AdapterInfo.PCI_Vendor_ID);
    printf ("PCI Device ID: %4.4X\n", R128_AdapterInfo.PCI_Device_ID);
    printf ("PCI Revision ID: %2.2X\n", R128_AdapterInfo.PCI_Revision_ID);
    printf ("PCI Bus Number: %2.2X\n", R128_AdapterInfo.bus_num);
    printf ("PCI Device Number: %2.2X\n", R128_AdapterInfo.dev_num);
    printf ("Memory Aperture Base: %8.8X\n", R128_AdapterInfo.MEM_BASE);
    printf ("Virtual Memory Aperture Base: %8.8X\n", R128_AdapterInfo.virtual_MEM_BASE);
    printf ("I/O Register Aperture Base: %8.8X\n", R128_AdapterInfo.IO_BASE);
    printf ("Memory Mapped Register Base: %8.8X\n", R128_AdapterInfo.REG_BASE);
    printf ("Virtual Memory Mapped Register Base: %8.8X\n", R128_AdapterInfo.virtual_REG_BASE);
    printf ("BIOS Segment: %8.8X\n", R128_AdapterInfo.BIOS_SEG);
    printf ("Installed Memory (in Mb): %2.2d\n", R128_AdapterInfo.memsize/1048576);

    return;
}  // R128_PrintInfoStruct

 
/****************************************************************************
 * R128_PrintInfoStruct                                                     *
 *  Function: Print AdapterInfo Structure to a file                         *
 *    Inputs: none                                                          *
 *   Outputs: none                                                          *
 ****************************************************************************/

void R128_PrintInfoStruct2File (void)
{
    FILE *fp;

    fp = fopen ("r128info.txt", "w");

    fprintf (fp, "\nAdapterInfo:\n");
    fprintf (fp, "PCI_Vendor_ID: %4.4X\n", R128_AdapterInfo.PCI_Vendor_ID);
    fprintf (fp, "PCI_Device_ID: %4.4X\n", R128_AdapterInfo.PCI_Device_ID);
    fprintf (fp, "PCI_Revision_ID: %2.2X\n", R128_AdapterInfo.PCI_Revision_ID);
    fprintf (fp, "Installed Memory: %8.8X\n", R128_AdapterInfo.memsize);
    fprintf (fp, "MEM_BASE: %8.8X\n", R128_AdapterInfo.MEM_BASE);
    fprintf (fp, "virtual_MEM_BASE: %8.8X\n", R128_AdapterInfo.virtual_MEM_BASE);
    fprintf (fp, "IO_BASE: %8.8X\n", R128_AdapterInfo.IO_BASE);
    fprintf (fp, "REG_BASE: %8.8X\n", R128_AdapterInfo.REG_BASE);
    fprintf (fp, "virtual_REG_BASE: %8.8X\n", R128_AdapterInfo.virtual_REG_BASE);
    fprintf (fp, "BIOS_SEG: %8.8X\n", R128_AdapterInfo.BIOS_SEG);
    fprintf (fp, "FLAGS: %8.8X\n", R128_AdapterInfo.FLAGS);
    fprintf (fp, "xres: %4.4X\n", R128_AdapterInfo.xres);
    fprintf (fp, "yres: %4.4X\n", R128_AdapterInfo.yres);
    fprintf (fp, "bpp: %4.4X\n", R128_AdapterInfo.bpp);
    fprintf (fp, "pitch: %4.4X\n", R128_AdapterInfo.pitch);

    fclose (fp);
    return;
}  // R128_PrintInfoStruct2File


/****************************************************************************
 * R128_Detect                                                              *
 *  Function: Rage 128 chip detection.                                      *
 *    Inputs: none                                                          *
 *   Outputs: 1 - chip detected                                             *
 *            0 - chip not detected                                         *
 ****************************************************************************/

BYTE R128_Detect (void)
{
    WORD wval1, wval2;
    WORD bus, busmax, device;
    WORD dbus, ddevice;
    BYTE R128_installed = 0;
    DWORD temp;

    // clear out the R128_AdapterInfo structure
    memset (&R128_AdapterInfo, 0, sizeof (R128_AdapterInfo) );

    busmax = PCIInstallCheck ();

    if (busmax > 255)
    {
        // no PCI BIOS installed, return a failure.
        return (0);
    } // if

    // Let's go through the PCI Config space until we find an ATI card.
    for (bus = 0; bus <= busmax; bus += 1)
    {
        for (device = 0; device < 32; device += 1)
        {
            wval1 = GetPCIWord (bus, device, 0x0000);
            if (wval1 != ATI_PCI_VENDOR_ID) continue;

            // However, let's make sure it's a R128.
            wval2 = GetPCIWord (bus, device, 0x0002); // get DeviceID

            switch (wval2)
            {
                case 0x5245:
                case 0x5246:
                case 0x524B:
                case 0x524C:
                case 0x534B:
                case 0x534C:
                case 0x534D:
                case 0x5345:
                case 0x5346:
                case 0x5347:
                case 0x5041:
                case 0x5042:
                case 0x5043:
                case 0x5044:
                case 0x5045:
                case 0x5046:
                case 0x5047:
                case 0x5048:
                case 0x5049:
                case 0x504A:
                case 0x504B:
                case 0x504C:
                case 0x504D:
                case 0x504E:
                case 0x504F:
                case 0x5050:
                case 0x5051:
                case 0x5052:
                case 0x5053:
                case 0x5054:
                case 0x5055:
                case 0x5056:
                case 0x5057:
                case 0x5058:
                case 0x4C45:
                case 0x4C46:

                    R128_installed = 1;
                    dbus = bus;
                    ddevice = device;
                    break;

                default:       // no R128 bnased product detected, 
                               // so don't set R128_installed!
                               break;
            } // switch
        } // for
    } // for

    if (R128_installed == 0)
    {
        // no R128 found, return failure
        return (0);
    }

    // fill in AdapterInfo structure.
    R128_AdapterInfo.PCI_Vendor_ID = GetPCIWord (dbus, ddevice, 0x0000);
    R128_AdapterInfo.PCI_Device_ID = GetPCIWord (dbus, ddevice, 0x0002);
    R128_AdapterInfo.PCI_Revision_ID = GetPCIByte (dbus, ddevice, 0x0008);

    temp = GetPCIDword (dbus, ddevice, 0x0010) & 0xFFF00000;
    R128_AdapterInfo.MEM_BASE = temp;

    temp = GetPCIDword (dbus, ddevice, 0x0014) & 0xFFFFFF00;
    R128_AdapterInfo.IO_BASE = temp;

    temp = GetPCIDword (dbus, ddevice, 0x0018) & 0xFFFFFFF0;
    R128_AdapterInfo.REG_BASE = temp;

    // get the virtual address for the register base.
    R128_AdapterInfo.virtual_REG_BASE = phys_to_virt(R128_AdapterInfo.REG_BASE, (10*1024));

    // Before we can determine the virtual address of the frame buffer,
    // we need to know how much memory is installed.  Read CONFIG_MEMSIZE
    temp = regr (CONFIG_MEMSIZE);
    // the memory size is in bits [25:0], so we'll mask off [31:26]
    temp = temp & 0x03FFFFFF;
    R128_AdapterInfo.memsize = temp;

    R128_AdapterInfo.virtual_MEM_BASE = phys_to_virt(R128_AdapterInfo.MEM_BASE, temp);

    // store these numbers for future PCI Config queries
    R128_AdapterInfo.bus_num = dbus;
    R128_AdapterInfo.dev_num = ddevice;

    return (1);

} // R128_Detect


/****************************************************************************
 * R128_FindRom                                                             *
 *  Function: Find ATI ROM segment.                                         *
 *    Inputs: none                                                          *
 *   Outputs: 1 - chip detected                                             *
 *            0 - chip not detected                                         *
 ****************************************************************************/

BYTE R128_FindRom (void)
{
    DWORD segstart;                     // ROM start address
    char *rom_base;                     // base of ROM segment
    char *rom;                          // offset pointer
    int stage;                          // stage 4 - done all 3 detections
    int i;                              // loop counter
    char ati_rom_sig[] = "761295520";   // ATI signature
    char R128_sig[] = "R128";           // Rage 128 signature
    char M3_sig[] = "M3";               // Rage Mobility 128 signature
    BYTE flag = 0;                      // assume R128 not found

    // Traverse ROM, looking at every 4 KB segment for ROM ID (stage 1).
    for (segstart = 0x000C0000; segstart < 0x000F0000; segstart += 0x00001000)
    {
        stage = 1;
        rom_base = (char *) segstart;
        if ((*rom_base == 0x55) && (*(rom_base + 1) == 0xAA))
        {
            stage = 2;                  // ROM found
        } // if
        if (stage != 2) continue;       // ROM ID not found.
        rom = rom_base;

        // Search for ATI signature (stage 2).
        for (i = 0; (i < 128 - strlen (ati_rom_sig)) && (stage != 3); i++)
        {
            if (ati_rom_sig[0] == *rom)
            {
                if (strncmp (ati_rom_sig, rom, strlen (ati_rom_sig)) == 0)
                {
                    stage = 3;          // Found ATI signature.
                } // if
            } // if
            rom++;
        } // for
        if (stage != 3) continue;       // ATI signature not found.
        rom = rom_base;

        // Search for R128 signature (stage 3).
        for (i = 0; (i < 512) && (stage != 4); i++)
        {
            if (R128_sig[0] == *rom)
            {
                if (strncmp (R128_sig, rom, strlen (R128_sig)) == 0)
                {
                    stage = 4;          // Found R128 signature.
                } // if
            } // if
            if (M3_sig[0] == *rom)
            {
                if (strncmp (M3_sig, rom, strlen (M3_sig)) == 0)
                {
                    stage = 4;          // Found M3 signature.
                } // if
            } // if
            rom++;
        } // for
        if (stage != 4) continue;       // R128 or M3 signature not found.

        // Successful! (stage 4)
        //fprintf (op, "\nRage 128 BIOS located at segment %4.4X\n", rom_base);
        // Fill in AdapterInfo struct accordingly
        R128_AdapterInfo.BIOS_SEG = (DWORD) rom_base;
        flag = 1;                       // Return value set to successful.
        break;
    } // for

    return (flag);

} // R128_FindRom


/****************************************************************************
 * R128_RegTest                                                             *
 *  Function: Uses BIOS_0_SCRATCH to test read/write functionality          *
 *    Inputs: NONE                                                          *
 *   Outputs: 0 - unsuccessful                                              *
 *            1 - successful                                                *
 ****************************************************************************/

BYTE R128_RegTest (void)
{
    DWORD save_value;
    BYTE flag = 0;

    // Save old value.
    save_value = regr (BIOS_0_SCRATCH);

    // Test odd bits for readability.
    regw (BIOS_0_SCRATCH, 0x55555555);
    if (regr (BIOS_0_SCRATCH) == 0x55555555)
    {
        // Test even bits for readability.
        regw (BIOS_0_SCRATCH, 0xAAAAAAAA);
        if (regr (BIOS_0_SCRATCH) == 0xAAAAAAAA)
        {
            flag = 1;
        } // if
    } // if

    // Restore old value.
    regw (BIOS_0_SCRATCH, save_value);

    return (flag);

} // R128_RegTest


/****************************************************************************
 * R128_GetBPPValue                                                         *
 *  Function: Get destination bpp format value based on bpp.                *
 *    Inputs: bpp - bit per pixel count                                     *
 *   Outputs: destination bpp format value                                  *
 ****************************************************************************/

DWORD R128_GetBPPValue (WORD bpp)
{
    switch (bpp)
    {
        case 8:     return (DST_8BPP);
                    break;
        case 15:    return (DST_15BPP);
                    break;
        case 16:    return (DST_16BPP);
                    break;
        case 24:    return (DST_24BPP);
                    break;
        case 32:    return (DST_32BPP);
                    break;
        default:    return (0);
                    break;
    }

    return (0);
}   // R128_GetBPPValue


/****************************************************************************
 * R128_GetBPPFromValue                                                     *
 *  Function: Get bpp count based on destination bpp format.                *
 *    Inputs: value - destination bpp format                                *
 *   Outputs: bit per pixel count                                           *
 ****************************************************************************/

WORD R128_GetBPPFromValue (DWORD value)
{

    switch (value)
    {
        case DST_8BPP:  return 8;
                        break;
        case DST_15BPP: return 15;
                        break;
        case DST_16BPP:
        case DST_16BPP_YVYU422:
        case DST_16BPP_VYUY422: return 16;
                                break;
        case DST_24BPP: return 24;
                        break;
        case DST_32BPP: return 32;
                        break;
        default:        return 0;
                        break;
    }

}   // R128_GetBPPFromValue ()


/****************************************************************************
 * R128_Delay                                                               *
 *  Function: Delay for given number of ticks.                              *
 *    Inputs: ticks - count of ticks to delay                               *
 *   Outputs: none                                                          *
 ****************************************************************************/

void R128_Delay (WORD ticks)
{
    WORD i;
    DWORD start, end;

    for (i=0;i<ticks;i++)
    {
        start = *((WORD *)(DOS_TICK_ADDRESS));
        end = start;
        while (end == start)
        {
            end = *((WORD *)(DOS_TICK_ADDRESS));
        }
    }
    return;
} // R128_Delay ()

