                TITLE   'Listing C-1'
                NAME    VideoID
                PAGE    55,132

;
; Name:         VideoID
;
; Function:     Detects the presence of various video subsystems and associated
;               monitors.
;
; Caller:       Microsoft C:
;
;                       void VideoID(VIDstruct);
;
;                       struct
;                       {
;                         char VideoSubsystem;
;                         char Display;
;                       }
;                               *VIDstruct[2];
;
;               Subsystem ID values:
;                                0  = (none)
;                                1  = MDA
;                                2  = CGA
;                                3  = EGA
;                                4  = MCGA
;                                5  = VGA
;                               80h = HGC
;                               81h = HGC+
;                               82h = Hercules InColor
;
;               Display types:   0  = (none)
;                                1  = MDA-compatible monochrome
;                                2  = CGA-compatible color
;                                3  = EGA-compatible color
;                                4  = PS/2-compatible monochrome
;                                5  = PS/2-compatible color
;                                
;
;       The values returned in VIDstruct[0].VideoSubsystem and
;       VIDstruct[0].Display indicate the currently active subsystem.
;

;ARGpVID         EQU     word ptr [bp+4] ; stack frame addressing
; FAR calls only
ARGpVID         EQU     word ptr [bp+6] ; stack frame addressing


VIDstruct       STRUC                   ; corresponds to C data structure

Video0Type      DB      ?               ; first subsystem type
Display0Type    DB      ?               ; display attached to first subsystem

Video1Type      DB      ?               ; second subsystem type
Display1Type    DB      ?               ; display attached to second subsystem

VIDstruct       ENDS


Device0         EQU     word ptr Video0Type[di]
Device1         EQU     word ptr Video1Type[di]


MDA             EQU     1               ; subsystem types               
CGA             EQU     2
EGA             EQU     3
MCGA            EQU     4
VGA             EQU     5
HGC             EQU     80h
HGCPlus         EQU     81h
InColor         EQU     82h

MDADisplay      EQU     1               ; display types
CGADisplay      EQU     2
EGAColorDisplay EQU     3
PS2MonoDisplay  EQU     4
PS2ColorDisplay EQU     5

TRUE            EQU     1
FALSE           EQU     0


DGROUP          GROUP   _DATA

_TEXT           SEGMENT byte public 'CODE'
                ASSUME  cs:_TEXT,ds:DGROUP

                PUBLIC  _VideoID
_VideoID        PROC    near

                push    bp              ; preserve caller registers
                mov     bp,sp
                push    si
                push    di

; initialize the data structure that will contain the results

                mov     di,ARGpVID      ; DS:DI -> start of data structure

                mov     Device0,0       ; zero these variables
                mov     Device1,0

; look for the various subsystems using the subroutines whose addresses are
;  tabulated in TestSequence; each subroutine sets flags in TestSequence
;  to indicate whether subsequent subroutines need to be called

                mov     byte ptr CGAflag,TRUE
                mov     byte ptr EGAflag,TRUE
                mov     byte ptr Monoflag,TRUE

                mov     cx,NumberOfTests
                mov     si,offset DGROUP:TestSequence

L01:            lodsb                   ; AL := flag
                test    al,al
                lodsw                   ; AX := subroutine address
                jz      L02             ; skip subroutine if flag is false

                push    si
                push    cx
                call    ax              ; call subroutine to detect subsystem
                pop     cx
                pop     si

L02:            loop    L01

; determine which subsystem is active

                call    FindActive

                pop     di              ; restore caller registers and return
                pop     si
                mov     sp,bp
                pop     bp
                ret

_VideoID        ENDP


;
; FindPS2
;
;       This subroutine uses INT 10H function 1Ah to determine the video BIOS
;        Display Combination Code (DCC) for each video subsystem present.
;

FindPS2         PROC    near

                mov     ax,1A00h
                int     10h             ; call video BIOS for info

                cmp     al,1Ah
                jne     L13             ; exit if function not supported (i.e.,
                                        ;  no MCGA or VGA in system)

; convert BIOS DCCs into specific subsystems & displays

                mov     cx,bx
                xor     bh,bh           ; BX := DCC for active subsystem

                or      ch,ch
                jz      L11             ; jump if only one subsystem present

                mov     bl,ch           ; BX := inactive DCC
                add     bx,bx
                mov     ax,[bx+offset DGROUP:DCCtable]

                mov     Device1,ax

                mov     bl,cl
                xor     bh,bh           ; BX := active DCC

L11:            add     bx,bx
                mov     ax,[bx+offset DGROUP:DCCtable]

                mov     Device0,ax

; reset flags for subsystems that have been ruled out

                mov     byte ptr CGAflag,FALSE
                mov     byte ptr EGAflag,FALSE
                mov     byte ptr Monoflag,FALSE

                lea     bx,Video0Type[di]  ; if the BIOS reported an MDA ...
                cmp     byte ptr [bx],MDA
                je      L12

                lea     bx,Video1Type[di]
                cmp     byte ptr [bx],MDA
                jne     L13

L12:            mov     word ptr [bx],0    ; ... Hercules can't be ruled out
                mov     byte ptr Monoflag,TRUE

L13:            ret

FindPS2         ENDP


;
; FindEGA
;
; Look for an EGA.  This is done by making a call to an EGA BIOS function
;  which doesn't exist in the default (MDA, CGA) BIOS.

FindEGA         PROC    near            ; Caller:       AH = flags
                                        ; Returns:      AH = flags
                                        ;               Video0Type and
                                        ;                Display0Type updated

                mov     bl,10h          ; BL := 10h (return EGA info)
                mov     ah,12h          ; AH := INT 10H function number
                int     10h             ; call EGA BIOS for info
                                        ; if EGA BIOS is present,
                                        ;  BL <> 10H
                                        ;  CL = switch setting
                cmp     bl,10h
                je      L22             ; jump if EGA BIOS not present

                mov     al,cl
                shr     al,1            ; AL := switches/2
                mov     bx,offset DGROUP:EGADisplays
                xlat                    ; determine display type from switches
                mov     ah,al           ; AH := display type
                mov     al,EGA          ; AL := subystem type
                call    FoundDevice

                cmp     ah,MDADisplay
                je      L21             ; jump if EGA has a monochrome display

                mov     CGAflag,FALSE   ; no CGA if EGA has color display
                jmp     short L22

L21:            mov     Monoflag,FALSE  ; EGA has a mono display, so MDA and
                                        ;  Hercules are ruled out
L22:            ret

FindEGA         ENDP


;
; FindCGA
;
;       This is done by looking for the CGA's 6845 CRTC at I/O port 3D4H.
;

FindCGA         PROC    near            ; Returns:      VIDstruct updated

                mov     dx,3D4h         ; DX := CRTC address port
                call    Find6845
                jc      L31             ; jump if not present

                mov     al,CGA
                mov     ah,CGADisplay
                call    FoundDevice

L31:            ret

FindCGA         ENDP


;
; FindMono
;
;       This is done by looking for the MDA's 6845 CRTC at I/O port 3B4H.  If
;       a 6845 is found, the subroutine distinguishes between an MDA
;       and a Hercules adapter by monitoring bit 7 of the CRT Status byte.
;       This bit changes on Hercules adapters but does not change on an MDA.
;
;       The various Hercules adapters are identified by bits 4 through 6 of
;       the CRT Status value:
;
;               000b = HGC
;               001b = HGC+
;               101b = InColor card
;

FindMono        PROC    near            ; Returns:      VIDstruct updated

                mov     dx,3B4h         ; DX := CRTC address port
                call    Find6845
                jc      L44             ; jump if not present

                mov     dl,0BAh         ; DX := 3BAh (status port)
                in      al,dx
                and     al,80h
                mov     ah,al           ; AH := bit 7 (vertical sync on HGC)

                mov     cx,8000h        ; do this 32768 times 
L41:            in      al,dx
                and     al,80h          ; isolate bit 7
                cmp     ah,al
                loope   L41             ; wait for bit 7 to change

                jne     L42             ; if bit 7 changed, it's a Hercules

                mov     al,MDA          ; if bit 7 didn't change, it's an MDA
                mov     ah,MDADisplay
                call    FoundDevice
                jmp     short L44

L42:            in      al,dx
                mov     dl,al           ; DL := value from status port

                mov     ah,MDADisplay   ; assume it's a monochrome display

                mov     al,HGC          ; look for an HGC
                and     dl,01110000b    ; mask off bits 4 thru 6
                jz      L43

                mov     al,HGCPlus      ; look for an HGC+
                cmp     dl,00010000b
                je      L43             ; jump if it's an HGC+

                mov     al,InColor      ; it's an InColor card
                mov     ah,EGAColorDisplay

L43:            call    FoundDevice

L44:            ret

FindMono        ENDP


;
; Find6845
;
;       This routine detects the presence of the CRTC on a MDA, CGA or HGC.
;       The technique is to write and read register 0Fh of the chip (cursor
;       low).  If the same value is read as written, assume the chip is
;       present at the specified port addr.
;

Find6845        PROC    near            ; Caller:       DX = port addr
                                        ; Returns:      cf set if not present
                mov     al,0Fh
                out     dx,al           ; select 6845 reg 0Fh (Cursor Low)
                inc     dx
                in      al,dx           ; AL := current Cursor Low value
                mov     ah,al           ; preserve in AH
                mov     al,66h          ; AL := arbitrary value
                out     dx,al           ; try to write to 6845

                mov     cx,100h
L51:            loop    L51             ; wait for 6845 to respond

                in      al,dx
                xchg    ah,al           ; AH := returned value
                                        ; AL := original value
                out     dx,al           ; restore original value

                cmp     ah,66h          ; test whether 6845 responded
                je      L52             ; jump if it did (cf is reset)

                stc                     ; set carry flag if no 6845 present

L52:            ret     
        
Find6845        ENDP


;
; FindActive
;
;       This subroutine stores the currently active device as Device0.  The
;       current video mode determines which subsystem is active.
;

FindActive      PROC    near

                cmp     word ptr Device1,0
                je      L63                     ; exit if only one subsystem

                cmp     Video0Type[di],4        ; exit if MCGA or VGA present
                jge     L63                     ;  (INT 10H function 1AH
                cmp     Video1Type[di],4        ;  already did the work)
                jge     L63

                mov     ah,0Fh
                int     10h                     ; AL := current BIOS video mode

                and     al,7
                cmp     al,7                    ; jump if monochrome
                je      L61                     ;  (mode 7 or 0Fh)

                cmp     Display0Type[di],MDADisplay
                jne     L63                     ; exit if Display0 is color
                jmp     short L62

L61:            cmp     Display0Type[di],MDADisplay
                je      L63                     ; exit if Display0 is monochrome

L62:            mov     ax,Device0              ; make Device0 currently active
                xchg    ax,Device1
                mov     Device0,ax

L63:            ret

FindActive      ENDP


;
; FoundDevice
;
;       This routine updates the list of subsystems.
;

FoundDevice     PROC    near                    ; Caller:    AH = display #
                                                ;            AL = subsystem #
                                                ; Destroys:  BX
                lea     bx,Video0Type[di]
                cmp     byte ptr [bx],0
                je      L71                     ; jump if 1st subsystem

                lea     bx,Video1Type[di]       ; must be 2nd subsystem

L71:            mov     [bx],ax                 ; update list entry
                ret

FoundDevice     ENDP

_TEXT           ENDS


_DATA           SEGMENT word public 'DATA'

EGADisplays     DB      CGADisplay      ; 0000b, 0001b  (EGA switch values)
                DB      EGAColorDisplay ; 0010b, 0011b
                DB      MDADisplay      ; 0100b, 0101b
                DB      CGADisplay      ; 0110b, 0111b
                DB      EGAColorDisplay ; 1000b, 1001b
                DB      MDADisplay      ; 1010b, 1011b

DCCtable        DB      0,0             ; translate table for INT 10h func 1Ah
                DB      MDA,MDADisplay
                DB      CGA,CGADisplay
                DB      0,0
                DB      EGA,EGAColorDisplay
                DB      EGA,MDADisplay
                DB      0,0
                DB      VGA,PS2MonoDisplay
                DB      VGA,PS2ColorDisplay
                DB      0,0
                DB      MCGA,EGAColorDisplay
                DB      MCGA,PS2MonoDisplay
                DB      MCGA,PS2ColorDisplay

TestSequence    DB      TRUE            ; this list of flags and addresses
                DW      FindPS2         ;  determines the order in which this
                                        ;  program looks for the various
EGAflag         DB      ?               ;  subsystems
                DW      FindEGA

CGAflag         DB      ?
                DW      FindCGA

Monoflag        DB      ?
                DW      FindMono

NumberOfTests   EQU     ($-TestSequence)/3

_DATA           ENDS

                END

