' S3 VGA video determination demo
'
'Copyright April 2, 1992:  George Spafford
'
'Use int 10H, function 1AH and subfunction 00H
'
'----------------------------------------------


TYPE RegType
	 AX    AS INTEGER
	 bx    AS INTEGER
	 cx    AS INTEGER
	 dx    AS INTEGER
	 bp    AS INTEGER
	 si    AS INTEGER
	 DI    AS INTEGER
	 flags AS INTEGER
END TYPE

TYPE RegTypeX
	 AX    AS INTEGER
	 bx    AS INTEGER
	 cx    AS INTEGER
	 dx    AS INTEGER
	 bp    AS INTEGER
	 si    AS INTEGER
	 DI    AS INTEGER
	 flags AS INTEGER
	 ds    AS INTEGER
	 ES    AS INTEGER
END TYPE

DECLARE SUB INTERRUPT (intnum AS INTEGER, inreg AS RegType, outreg AS RegType)
DECLARE SUB INTERRUPTX (intnum AS INTEGER, inregX AS RegTypeX, outregX AS RegTypeX)
DECLARE FUNCTION MakeWord& (Num AS INTEGER)

DIM InRegs AS RegType
DIM OutRegs AS RegType
DIM InRegsX AS RegTypeX
DIM OutRegsX AS RegTypeX

DEFINT A-Z

CLS
PRINT "S3 Software VGA Identification Demo."
PRINT

intnum = &H10
InRegs.AX = &H1A * 256          'load AH (primary)

'the previous load cleared AL but oh well, this is a
'demo.  A value * 256 loads AH but clears AL.  To
'load AH without clearing AL you would do something
'like InRegs.AX=InRegs.AX OR (&H1A * 256)  The OR
'operator allows AH to be loaded independently.

'We are calling interrupt 10H with function 1AH with
'subfunction 0.  That's why we really did  not need
'to load AL since it was already cleared, but I wanted
'you to see how to load AH and AL independently.

CALL INTERRUPT(intnum, InRegs, OutRegs)

'Now, this function must return &H1A in the AL register.
'If it does not, then a VGA card is not in the PC and we
'will end the program.  The MakeWord&() function takes an
'a integer value an turns it into a long integer just
'in case the integer has its sign bit used.

AL = MakeWord&(OutRegs.AX) AND &HFF
IF AL <> &H1A THEN
   PRINT "This test requires VGA"
   END
END IF

'Now, BL contains the information for the currently
'active video adapter in the PC.  Since most of us
'only have one, it will probably be the only one
'identified when you run the sample program.
'Note, we once again make the register a long value
'before we pull out the 8 bit register we want.

PRINT "Active Video Adapter:"
PRINT "     ";
BL = MakeWord&(OutRegs.bx) AND &HFF
SELECT CASE BL
	   CASE &HFF
			PRINT "Unknown video card"
	   CASE &H0
			PRINT "No adapter located"
	   CASE &H1
			PRINT "MDA adapter with monochrome monitor"
	   CASE &H2
			PRINT "CGA adapter with color monitor"
	   CASE &H4
			PRINT "EGA adapter with EGA or multisynch monitor"
	   CASE &H5
			PRINT "EGA adapter with a monochrome monitor"
	   CASE &H7
			PRINT "VGA adapter with an analog monochrome monitor"
	   CASE &H8
			PRINT "VGA adapter with an analog color monitor"
END SELECT

'In the case of dual video adapter systems, BH contains
'information for the inactive video adapter.

PRINT
PRINT "Inactive Video Adapter:"
PRINT "     ";
BH = MakeWord&(OutRegs.bx) \ 256
SELECT CASE BH
	   CASE &HFF
			PRINT "Unknown video card"
	   CASE &H0
			PRINT "No adapter located"
	   CASE &H1
			PRINT "MDA adapter with monochrome monitor"
	   CASE &H2
			PRINT "CGA adapter with color monitor"
	   CASE &H4
			PRINT "EGA adapter with EGA or multisynch monitor"
	   CASE &H5
			PRINT "EGA adapter with a monochrome monitor"
	   CASE &H7
			PRINT "VGA adapter with an analog monochrome monitor"
	   CASE &H8
			PRINT "VGA adapter with an analog color monitor"
END SELECT


'Now, let us hunt for the dreaded VESA extensions.
'By using interrupt 10H with function 4FH and subfunction
'00H, we can look for a VESA compliant SVGA. 

intnum = &H10
InRegsX.AX = &H0                         'load AL FIRST!!
InRegsX.AX = InRegsX.AX OR (&H4F * 256)   'load AH (primary)

CALL INTERRUPTX(intnum, InRegsX, OutRegsX)

'For a detailed look at splitting high and low bytes,
'look at the S3DATE.BAS example program.

AH = MakeWord&(OutRegsX.AX) \ 256
AL = MakeWord&(OutRegsX.AX) AND &HFF

'if AL contains 4fH and AH contains 0H then the
'system is VESA compliant. 

IF AL = &H4F AND AH = &H0 THEN

   'The DEF SEG line changes the current memory
   'segment to that which is specified in the ES
   'segment register.

   DEF SEG = (OutRegsX.ES)
   Vesa$ = ""

   'Next, we read starting at the offset specified
   'in DI for the VESA signature.  In order to
   'guarantee that the system is indeed a VESA
   'system and the AL and AH aren't incidentally
   'correct, DI to DI + 3 have the word "VESA"
   'distributed through each offset.  In other
   'words, the offset DI has "V", DI + 1 has "E" and
   'so forth.

   FOR a = 0 TO 3
	   B = PEEK(OutRegsX.DI + a)
	   Vesa$ = Vesa$ + CHR$(B)
   NEXT a
   IF Vesa$ = "VESA" THEN
	  PRINT
	  PRINT "VESA extensions confirmed"
	  PRINT
	 'Next, the High Level Version Number can be pulled
	 'out at offset DI + 4.
	 HLV = PEEK(OutRegsX.DI + &H4)
	 'The Low Level Version Number can be pulled out
	 'at the offset DI + 5.
	 LLV = PEEK(OutRegsX.DI + &H5)
	 'DEF SEG by itself will return the Segment address
	 'to the default segment.
	 DEF SEG
	 PRINT "Vesa Signature..............:  "; Vesa$
	 PRINT "High Level Version Number...:  "; HLV
	 PRINT "Low Level Version Number....:  "; LLV
   END IF
END IF

IF Vesa$ <> "VESA" THEN
   PRINT
   PRINT "Not VESA Compliant"
   PRINT
END IF


'When you performed the function call, if AH contains
'&H1, then some type of error occurred.

IF AL = &H4F AND AH = &H1 THEN
   PRINT : PRINT
   PRINT "Error executing VESA identification."
END IF

PRINT : PRINT

'Well, another thrilling example has come to a close.
'I do not know about you, but I am so excited I could
'fall over.

END


DEFSNG A-Z
FUNCTION MakeWord& (Num AS INTEGER)
	IF Num < 0 THEN
	   MakeWord& = 65536 + Num
	ELSE
	   MakeWord& = Num
	END IF
END FUNCTION

