/******************************************************************************
 * init.c - Chapter 3 sample code                                             *
 *                                                                            *
 * This module contains primitives for mach64 detection and testing.          *
 * Also included are functions to save and restore the old video mode.        *
 * This information is stored in the global variable OLD_MODE.                *
 *                                                                            *
 * Copyright (c) 1994-1998 ATI Technologies Inc.  All rights reserved.        *
 ******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <i86.h>
#include "defines.h"
#include "main.h"

/******************************************************************************
 * is_mach64_rom                                                              *
 *  Function: This function performs 3 detections: 1 - ROM detection          *
 *                                                 2 - ATI card detection     *
 *                                                 3 - mach64 detection       *
 *    Inputs: NONE                                                            *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int is_mach64_rom (void)
{
    unsigned long 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 mach64_sig1[] = "MACH64";      // mach64 signature
    char mach64_sig2[] = "GXCX";        // mach64 signature older ROMs
    int flag = 0;                       // assume mach64 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 mach64 signature (stage 3).
        for (i = 0; (i < 1024) && (stage != 4); i++)
        {
            if ((mach64_sig1[0] == *rom) || (mach64_sig2[0] == *rom))
            {
                if ((strncmp (mach64_sig1, rom, strlen (mach64_sig1)) == 0) ||
                    (strncmp (mach64_sig2, rom, strlen (mach64_sig2)) == 0))
                {
                    stage = 4;          // Found mach64 signature.
                } // if
            } // if
            rom++;
        } // for
        if (stage != 4) continue;       // mach64 signature not found.

        // Successful! (stage 4)
        printf ("mach64 BIOS located at segment %4.4X\n", rom_base);
        flag = 1;                       // Return value set to successful.
        break;
    } // for

    return (flag);

} // is_mach64_rom


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

int reg_test (void)
{
    unsigned long save_value;
    unsigned long temp;
    long scratch1;                      // I/O select 0x11, DWORD offset 0x21
    int flag = 0;

    // Calculate scratch register 1 address.
    if (IO_DATA.io_type == FIXED)
    {
        scratch1 = (0x11 << 10) + IO_DATA.io_base;
    }
    else
    {
        // Relocatable IO.
        scratch1 = (0x21 << 2) + IO_DATA.io_base;
    } // if

    // Save old value.
    save_value = inpd (scratch1);

    // Test odd bits for readability.
    outpd (scratch1, 0x55555555);
    if (inpd (scratch1) == 0x55555555)
    {
        // Test even bits for readability.

        outpd (scratch1, 0xAAAAAAAA);
        if (inpd (scratch1) == 0xAAAAAAAA)
        {
            flag = 1;
        } // if
    } // if

    // Restore old value.
    outpd (scratch1, save_value);

    return (flag);

} // reg_test


/******************************************************************************
 * init_graphics                                                              *
 *  Function: Performs detection of ATI mach64                                *
 *    Inputs: NONE                                                            *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int init_graphics (void)
{
    // Step 1 - Find ATI mach64 ROM and its ROM segment by scanning through
    // ROM segments C000h - DF00h.  Match ROM ID, ATI sig, and mach64 string.
    if (!is_mach64_rom ())
    {
        printf ("WARNING: A mach64 BIOS was not found.\n");
        exit (1);
    } // if

    // Step 2 - Call ROM to find I/O base address and type.
    if (!short_query ())                // ATI BIOS service 12h.
    {
        printf ("WARNING: Error in getting I/O Base Address.\n");
        exit (1);
    } // if

    // Step 3 - Perform R/W test on SCRATCH_REG1.
    if (!reg_test ())
    {
        printf ("WARNING: Error writing and reading I/O register.\n");
        exit (1);
    } // if

    return (1);

} // init_graphics


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

int 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                                       *
 ******************************************************************************/

int 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;

    // Set default resolution and colour depth in global variables.
    GMODE_RES = 640;
    GCLR_DEPTH = 8;

    // Override defaults with valid command line arguments.
    for (i = 1; i < argc; i++)
    {
        // Check for valid resolution options.
        if ((strcmp (argv[i], "200") == 0) ||
            (strcmp (argv[i], "240") == 0) ||
            (strcmp (argv[i], "640") == 0) ||
            (strcmp (argv[i], "800") == 0) ||
            (strcmp (argv[i], "1024") == 0) ||
            (strcmp (argv[i], "1280") == 0) ||
            (strcmp (argv[i], "1600") == 0))
        {
            GMODE_RES = atoi (argv[i]);
        } // if

        // Check for valid colour depth options.
        if ((strcmp (argv[i], "4") == 0) ||
            (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))
        {
            GCLR_DEPTH = atoi (argv[i]);
        } // if
    } // for

    return;

} // process_command_line
