
/*----------------------------------------------------------------------\
|       Marcus W. Johnson 1990.                                         |
|                                                                       |
|       Code to identify video display adaptors and display devices     |
|                                                                       |
|       Adapted from:   Programmer's Guide to PC and PS/2 Video Systems |
|                       Richard Wilton                                  |
|                       Microsoft Press                                 |
|                       Redmond, Washington                             |
\----------------------------------------------------------------------*/

#include        <dos.h>
#include        "video.h"

enum    boolean
        {
        NO,
        YES
        };

#define N_SYSTEMS       (2)

static struct video     Device[ N_SYSTEMS ];

/*----------------------------------------------------------------------\
|       Detects whether or not a given I/O address is that of a CRT     |
|       Controller; the Cursor Location Low register of the alleged     |
|       CRTC is written with an arbitrary value (I used the one Wilton  |
|       uses in his Find6845 procedure), we wait an arbitrary period of |
|       time (I waited a millisecond), and we see if the value is still |
|       there, then this is probably the CRTC.                          |
\----------------------------------------------------------------------*/

static int      FindCRTC(int Port)
        {
        unsigned char   CursorLow;
        unsigned char   NewCursorLow;

        outportb(Port++, 0x0F);
        CursorLow = inportb(Port);
        outportb(Port, 0x66);
        delay(1);
        NewCursorLow = inportb(Port);
        outportb(Port, CursorLow);
        return (NewCursorLow == 0x66);
        }

/*----------------------------------------------------------------------\
|       Places the specified adaptor and monitor data in the next       |
|       unused element of the Device array.                             |
\----------------------------------------------------------------------*/

static void     FoundDevice(enum adaptor AType, enum monitor MType)
        {
        if (Device[ 0 ].VideoAdaptor == UnknownAdaptor)
                {
                Device[ 0 ].VideoAdaptor = AType;
                Device[ 0 ].VideoMonitor = MType;
                }
        else
                {
                Device[ 1 ].VideoAdaptor = AType;
                Device[ 1 ].VideoMonitor = MType;
                }
        }

/*----------------------------------------------------------------------\
|       Attempt to find a monochrome adaptor; attempts to detect a CRTC |
|       at I/O address at 0x3B4.  If this is successful, we read the    |
|       vertical sync bit, and wait a while to see a transition; if it  |
|       occurs, it's a plain monochrome display adaptor, otherwise it's |
|       a Hercules card of some sort.  What type is decided by bits 4-6 |
|       of the CRTC port.                                               |
\----------------------------------------------------------------------*/

static void     DetectMono(void)
        {
        if (FindCRTC(0x3B4))
                {
                auto unsigned char      VSync;
                auto unsigned int       k;
                auto enum boolean       FoundIt;

                VSync = inportb(0x3BA) & 0x80;
                FoundIt = NO;
                for (k = 0; k < 0x8000; k++)
                        {
                        if (VSync != (inportb(0x3BA) & 0x80))
                                {
                                switch (inportb(0x3BA) & 0x70)
                                        {
                                        case    0x10:
                                                FoundDevice(HGCPlus, MDA);
                                                break;
                                        case    0x50:
                                                FoundDevice(HerculesInColor,
                                                        EGAColor);
                                                break;
                                        default:
                                                FoundDevice(HGC, MDA);
                                                break;
                                        }
                                FoundIt = YES;
                                break;
                                }
                        }
                if (FoundIt == NO)
                        FoundDevice(MDA, MDAMonochrome);
                }
        }

/*----------------------------------------------------------------------\
|       Attempt to find a CGA adaptor; if a CRTC is detected at I/O     |
|       address 3D4, must be CGA...                                     |
\----------------------------------------------------------------------*/

static void     DetectCGA(void)
        {
        if (FindCRTC(0x3D4))
                FoundDevice(CGA, CGAColor);
        }

/*----------------------------------------------------------------------\
|       Fills in the Device array and returns its address to the        |
|       caller, who can then examine its contents.                      |
\----------------------------------------------------------------------*/

struct video    *IdentifyVideo(void)
        {
        int             k;
        union REGS      r;

        for (k = 0; k < N_SYSTEMS; k++)
                {
                Device[ k ].VideoAdaptor = UnknownAdaptor;
                Device[ k ].VideoMonitor = UnknownMonitor;
                }

        /*--------------------------------------------------------------\
        |       Attempt to detect PS/2-type systems by making a BIOS    |
        |       call to get the video display combination from the      |
        |       video BIOS.  On return, the AL register is set to 1A,   |
        |       BL will contain the display code for the active display |
        |       and BH will contain the display code for the inactive   |
        |       display.  The BL and BH registers are used to index     |
        |       arrays containing codes for the appropriate display and |
        |       display adaptor.                                        |
        \--------------------------------------------------------------*/

        r.x.ax = 0x1A00;
        int86(0x10, &r, &r);
        if (r.h.al == 0x1A)
                {
                static struct video     DeviceList[ ] =
                        {
                        {       UnknownAdaptor, UnknownMonitor  },
                        {       MDA,            MDAMonochrome   },
                        {       CGA,            CGAColor        },
                        {       UnknownAdaptor, UnknownMonitor  },
                        {       EGA,            EGAColor        },
                        {       EGA,            MDAMonochrome   },
                        {       UnknownAdaptor, UnknownMonitor  },
                        {       VGA,            PS2Monochrome   },
                        {       VGA,            PS2Color        },
                        {       UnknownAdaptor, UnknownMonitor  },
                        {       MCGA,           EGAColor        },
                        {       MCGA,           PS2Monochrome   },
                        {       MCGA,           PS2Color        }
                        };

                if (r.h.bh != 0)
                        {
                        Device[ 1 ].VideoAdaptor =
                                DeviceList[ r.h.bh ].VideoAdaptor;
                        Device[ 1 ].VideoMonitor =
                                DeviceList[ r.h.bh ].VideoMonitor;
                        }
                Device[ 0 ].VideoAdaptor = DeviceList[ r.h.bl ].VideoAdaptor;
                Device[ 0 ].VideoMonitor = DeviceList[ r.h.bl ].VideoMonitor;
                if (Device[ 0 ].VideoAdaptor == MDA ||
                        Device[ 1 ].VideoAdaptor == MDA)
                        {

                        /*----------------------------------------------\
                        |       If either the active display or the     |
                        |       inactive display is identified as MDA,  |
                        |       we clear the system and display         |
                        |       information for that display; we need   |
                        |       to further identify the system as       |
                        |       possibly a Hercules card.               |
                        \----------------------------------------------*/

                        if (Device[ 0 ].VideoAdaptor == MDA)
                                {
                                Device[ 0 ].VideoAdaptor = UnknownAdaptor;
                                Device[ 0 ].VideoMonitor = UnknownMonitor;
                                }
                        else
                                {
                                Device[ 1 ].VideoAdaptor = UnknownAdaptor;
                                Device[ 1 ].VideoMonitor = UnknownMonitor;
                                }
                        DetectMono();
                        }
                }
        else
                {

                /*------------------------------------------------------\
                |       detect an EGA card; make a call to the BIOS to  |
                |       get the video subsystem configuration.  On      |
                |       return, BL will be set to 0, 1, 2 or 3 (which   |
                |       is the number of 64K blocks of RAM in addition  |
                |       to the default 64K block on the video card),    |
                |       and the least 4 bits of CL contain the          |
                |       configuration switch settings for the card.     |
                |       This includes information as to the display     |
                |       type.                                           |
                \------------------------------------------------------*/

                r.h.bl = 0x10;
                r.h.ah = 0x12;
                int86(0x10, &r, &r);
                if (r.h.bl != 0x10)
                        {
                        auto enum monitor       Display;
                        static enum monitor     EGADisplay[ ] =
                                {
                                CGAColor,       EGAColor,       MDAMonochrome
                                };

                        Display = EGADisplay[ (r.h.cl % 6) >> 1 ];
                        FoundDevice(EGA, Display);

                        /*----------------------------------------------\
                        |       If a monochrome display is found, any   |
                        |       other system must be color, and         |
                        |       vice-versa.                             |
                        \----------------------------------------------*/

                        if (Display == MDAMonochrome)
                                DetectCGA();
                        else
                                DetectMono();
                        }
                else
                        {
                        DetectCGA();
                        DetectMono();
                        }
                }

        /*--------------------------------------------------------------\
        |       Resolve discrepancies between systems with multiple     |
        |       display types and the current video state; not a        |
        |       problem if only one type was found, or one of the types |
        |       found was MCGA or VGA.  Basically, the active display,  |
        |       which is in Device[ 0 ], should match the color         |
        |       specification of the current video mode.  Incidently,   |
        |       the device swap is performed by the trick of using the  |
        |       mathematical properties of the exclusive or operator to |
        |       avoid having to declare a temporary holding variable.   |
        \--------------------------------------------------------------*/

        if (Device[ 1 ].VideoAdaptor != UnknownAdaptor &&
                Device[ 0 ].VideoAdaptor != VGA &&
                Device[ 0 ].VideoAdaptor != MCGA &&
                Device[ 1 ].VideoAdaptor != VGA &&
                Device[ 1 ].VideoAdaptor != MCGA)
                {
                r.h.ah = 0x0F;
                int86(0x10, &r, &r);
                if ((r.h.al & 7) == 7)
                        {
                        if (Device[ 0 ].VideoMonitor != MDAMonochrome)
                                {
                                Device[ 0 ].VideoMonitor ^=
                                        Device[ 1 ].VideoMonitor;
                                Device[ 1 ].VideoMonitor ^=
                                        Device[ 0 ].VideoMonitor;
                                Device[ 0 ].VideoMonitor ^=
                                        Device[ 1 ].VideoMonitor;
                                Device[ 0 ].VideoAdaptor ^=
                                        Device[ 1 ].VideoAdaptor;
                                Device[ 1 ].VideoAdaptor ^=
                                        Device[ 0 ].VideoAdaptor;
                                Device[ 0 ].VideoAdaptor ^=
                                        Device[ 1 ].VideoAdaptor;
                                }
                        }
                else
                        {
                        if (Device[ 0 ].VideoMonitor == MDAMonochrome)
                                {
                                Device[ 0 ].VideoMonitor ^=
                                        Device[ 1 ].VideoMonitor;
                                Device[ 1 ].VideoMonitor ^=
                                        Device[ 0 ].VideoMonitor;
                                Device[ 0 ].VideoMonitor ^=
                                        Device[ 1 ].VideoMonitor;
                                Device[ 0 ].VideoAdaptor ^=
                                        Device[ 1 ].VideoAdaptor;
                                Device[ 1 ].VideoAdaptor ^=
                                        Device[ 0 ].VideoAdaptor;
                                Device[ 0 ].VideoAdaptor ^=
                                        Device[ 1 ].VideoAdaptor;
                                }
                        }
                }
        return (Device);
        }
