/******************************************************************************
 * m64bios.c - Chapter 3 sample code                                          *
 *                                                                            *
 * This module contains functions pertaining to the mach64 BIOS function      *
 * calls.                                                                     *
 *                                                                            *
 * Copyright (c) 1994-1998 ATI Technologies Inc.  All rights reserved.        *
 ******************************************************************************/

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

/******************************************************************************
 * load_and_set_mode                                                          *
 *  Function: sets a graphics mode for the mach64 video card using            *
 *            mach64 BIOS call 02h.  Also sets up MODE_INFO global.           *
 *    Inputs: xres - spatial resolution (640, 800, 1024, 1280, 1600)          *
 *                   restricted by memory and monitor limitations             *
 *            bpp - pixel depth (8, 15, 16, 24, 32)  4bpp not supported       *
 *            pitch - horizontal pitch. 0 - 1024                              *
 *                                      1 - Don't change                      *
 *                                      2 - same as x resolution              *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int load_and_set_mode (int xres, int bpp, int pitch)
{
    union REGS r;

    memset (&r, 0, sizeof (r));
    r.w.ax = 0xA002;                    // Function 02h: Load and Set Mode.

    // Determine requested resolution.
    switch (xres)
    {
        case 200:
            r.h.ch = 0xE2;
            xres = 320;
            MODE_INFO.yres = 200;
            break;

        case 240:
            r.h.ch = 0xE3;
            xres = 320;
            MODE_INFO.yres = 240;
            break;

        case 640:
            r.h.ch = 0x12;
            MODE_INFO.yres = 480;
            break;

        case 800:
            r.h.ch = 0x6A;
            MODE_INFO.yres = 600;
            break;

        case 1024:
            r.h.ch = 0x55;
            MODE_INFO.yres = 768;
            break;

        case 1280:
            r.h.ch = 0x83;
            MODE_INFO.yres = 1024;
            break;

        case 1600:
            r.h.ch = 0x84;
            MODE_INFO.yres = 1200;
            break;

        default:
            printf ("Error: Unable to set specified resolution\n");
            return (0);
    } // switch
    MODE_INFO.xres = xres;

    // Determine requested pixel depth
    switch (bpp)
    {
        case 8:                         // 256 colours, palletized
            r.h.cl = 0x02;
            break;

        case 15:                        // 32768 colours
            r.h.cl = 0x03;
            if (QUERY_DATA.colour_depth_support & DEPTH_SUPPORT_555)
            {
                MODE_INFO.depth = DEPTH_SUPPORT_555;
            }
            else
            {
                printf ("Error: Pixel Depth 15 not supported\n");
                return (0);
            } // if
            break;

        case 16:                        // 65536 colours
            r.h.cl = 0x04;
            if (QUERY_DATA.colour_depth_support & DEPTH_SUPPORT_565)
            {
                MODE_INFO.depth = DEPTH_SUPPORT_565;
            }
            else
            {
                printf ("Error: Pixel Depth 16 not supported\n");
                return (0);
            } // if
            break;

        case 24:                        // 16777216 colours
            r.h.cl = 0x05;

            // Set support for the R,G,and B ordering according to
            // the colour depth supported or according to precedence
            // if more than one format is supported.

            if (QUERY_DATA.colour_depth_support & DEPTH_SUPPORT_RGB)
            {
                MODE_INFO.depth = DEPTH_SUPPORT_RGB;
            }
            else if (QUERY_DATA.colour_depth_support & DEPTH_SUPPORT_BGR)
            {
                MODE_INFO.depth = DEPTH_SUPPORT_BGR;
            }
            else
            {
                printf ("Error: Pixel Depth 24 not supported\n");
                return (0);
            } // if
            break;

        case 32:                        // 16777216 colours with alpha channel
            r.h.cl = 0x06;

            // Set support for the R,G,B, and A ordering according to
            // the colour depth supported or according to precedence
            // if more than one format is supported.

            if (QUERY_DATA.colour_depth_support & DEPTH_SUPPORT_RGBA)
            {
                MODE_INFO.depth = DEPTH_SUPPORT_RGBA;
            }
            else if (QUERY_DATA.colour_depth_support & DEPTH_SUPPORT_ARGB)
            {
                MODE_INFO.depth = DEPTH_SUPPORT_ARGB;
            }
            else if (QUERY_DATA.colour_depth_support & DEPTH_SUPPORT_BGRA)
            {
                MODE_INFO.depth = DEPTH_SUPPORT_BGRA;
            }
            else if (QUERY_DATA.colour_depth_support & DEPTH_SUPPORT_ABGR)
            {
                MODE_INFO.depth = DEPTH_SUPPORT_ABGR;
            }
            else
            {
                printf ("Error: Pixel Depth 32 not supported\n");
                return (0);
            } // if
            break;

        default:
            printf ("Error: Unable to set specified colour depth\n");
            return (0);
    } // switch
    MODE_INFO.bpp = bpp;

    // Determine requested pitch for the mode.

    switch (pitch)
    {
        case 0:                         // 1024.
            MODE_INFO.pitch = 1024;
            break;

        case 1:                         // Don't change.
            MODE_INFO.pitch = 0;
            r.h.cl += 0x40;
            break;

        case 2:                         // Pitch size same as X resolution.
            MODE_INFO.pitch = MODE_INFO.xres;
            r.h.cl += 0x80;
            break;

        default:
            printf ("Error: Unable to set specified pitch\n");
            return (0);
    } // switch

    // Call the BIOS to set the mode.

    int386 (0x10, &r, &r);
    if (r.h.ah)
    {
        return (0);                     // Error setting mode.
    }
    else
    {
        return (1);
    } // if

} // load_and_set_mode


/******************************************************************************
 * long_query                                                                 *
 *  Function: Fills entries of query_data structure.  mach64 BIOS 08h, 09h.   *
 *            Gets the size of the data by mach64 BIOS function 08h.          *
 *            Allocates memory in 1st MB of size obtained from 08h.  It       *
 *            is placed in the 1st MB so that a segment address can be        *
 *            obtained.  Then pointer to this buffer is passed through        *
 *            09h, to get the query information.  This information is         *
 *            copied to the global QUERY_DATA.  Finally the buffer is         *
 *            freed up.                                                       *
 *    Inputs: NONE                                                            *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int long_query (void)
{
    union REGS r;
    struct SREGS sr;
    unsigned short size;
    unsigned short buffer;
    unsigned short seg;
    char *buf;


    // mach64 function 0x08 - Return Query Device Structure size in bytes.
    memset (&r, 0, sizeof (r));
    r.w.ax = 0xA008;                    // Function 08h: Get Query Size.
    r.h.cl = 0x00;                      // Header size info only.
    int386 (0x10, &r, &r);
    size = 1 + (r.w.cx/16);             // Size of header in paragraphs.
    buffer = r.w.cx;

    if (r.h.ah)
    {
        printf ("Error: Function 08h unsuccessful\n");
        return (0);
    } // if

    // Allocate memory for buffer in 1st MB so we can get a segment and offset.
    memset (&r, 0, sizeof (r));
    r.h.ah = 0x48;                      // Allocate memory.
    r.w.bx = size;                      // Size of buffer in paragraphs.
    int386 (0x21, &r, &r);
    buffer = r.w.ax;                    // Assign buffer to returned address.
    

    // mach64 function 0x09 - Fill buffer with query information.
    memset (&r, 0, sizeof (r));
    r.w.ax = 0xA009;                    // Function 09h: Get Query Structure.
    r.h.cl = 0x00;                      // Header info only.
    r.w.dx = buffer;                    // Segment address.
    r.w.bx = 0x00;                      // Offset address.

    int386 (0x10, &r, &r);

    if (r.h.ah)
    {
        printf ("Error: Function 09h unsuccessful\n");
        return (0);
    } // if

    // Move contents into QUERY_DATA structure.
    buf = (char *) (buffer << 4);
    memcpy (&QUERY_DATA, buf, sizeof (QUERY_DATA));
    
    // Free up buffer.
    memset (&r, 0, sizeof (r));
    memset (&sr, 0, sizeof (sr));
    r.h.ah = 0x49;                      // Free memory.
    sr.es = buffer;                     // Segment buffer to free.
    int386x (0x21, &r, &r, &sr);

    return (1);

} // long_query


/******************************************************************************
 * short_query                                                                *
 *  Function: Gets I/O base address and type (relocatable or fixed) using     *
 *            mach64 BIOS function 12h.  Not to be confused with short        *
 *            query function 06h.  Global structure IO_DATA is filled.        *
 *    Inputs: NONE                                                            *
 *   Outputs: 0 - unsuccessful                                                *
 *            1 - successful                                                  *
 ******************************************************************************/

int short_query (void)
{
    union REGS r;

    r.w.ax = 0xA012;                    // Function 12h: Get I/O Base Address.
    r.w.cx = 0x0000;                    // Ensure backward compatibility.
    int386 (0x10, &r, &r);
    if (r.h.ah)
    {
        printf ("Error: function 12h unsuccessful\n");
        return (0);
    }
    else
    {
        IO_DATA.io_type = r.w.cx;
        IO_DATA.io_base = r.w.dx;
        return (1);
    } // if

} // short_query
