;  Source name     : INFO.ASM
;  Executable name : INFO.COM
;  Code model:     : Real mode flat model
;  Version         : 2.0
;  Created date    : 9/18/1999
;  Last update     : 9/19/1999
;  Author          : Jeff Duntemann
;  Description     : A utility to query and display information about
;                    the installed PC video adapter, programmed for
;                    real mode flat model, using NASM 0.98 and ALINK.
;                    This program demonstrates how lookup tables and
;                    numerous instructions are used.

[BITS 16]                   ; Set 16 bit code generation
[ORG 0100H]                 ; Set code start address to 100h (COM file)

[SECTION .text]             ; Section containing code

%include "MYLIB.MAC"   ; Load in screen control macro library

START:                      ; This is where program execution begins:
           call VidCheck    ; Initialize all video information variables

           Clear VidSegment,ClearAtom,VidBufSize  ; Clear the screen

           ; Here we display the name of the program and its author:
           Writeln IDString,LIDString      ; Display the program name
           Writeln AuthorStr,LAuthorStr    ; display the author name
           Newline

           ; Here we display the name of the installed video board:
           Write VidIDStr,LVidIDStr        ; Display the intro string
           mov BX,1         ; Select DOS file handle 1: Standard Output
           mov CX,27        ; The name strings are 27 bytes long
           mov DX,[BordName]; The string address is stored in BordName
           mov AH,40H       ; Service 40H: Write string to file
           int 21H          ; Call DOS to display to Standard Output
           Newline

           ; Here we display the segment address of the refresh buffer:
           Write OrgIDStr,LOrgIDStr     ; Display the intro string
           mov  AX,[VidSegment] ; AX gets the value to convert to a string
           mov  SI,DigitStr   ; String equivalent is written to DigitStr
           call Word2Str      ; Do the actual string conversion
           PokeChar DigitStr,'H',4   ; Append 'H' on the end of the string
           Writeln DigitStr,5 ;  and display the string equivalent

           ; Here we display the size of the current text font:
           Write FontSzStr,LFontSzStr   ; Display the intro string
           mov  AL,[FontSize] ; AL gets the value to convert to a string
           mov  SI,DigitStr   ; String equivalent is written to DigitStr
           call Byte2Str      ; Do the actual string conversion
           PokeChar DigitStr,'H',2   ; Append 'H' on the end of the string
           Writeln DigitStr,3 ;  and display the string equivalent

           ; Here we display the number of lines on the screen:
           Write ScrnLnStr,LScrnLnStr
           mov  AL,[VisibleY]   ; AL gets the value to convert to a string
           mov  SI,DigitStr   ; String equivalent is written to DigitStr
           call Byte2Str      ; Do the actual string conversion
           PokeChar DigitStr,'H',2   ; Append 'H' on the end of the string
           Writeln DigitStr,3 ;  and display the string equivalent

           ;Finally, we display the size of the video refresh buffer:
           Write BufSizStr,LBufSizStr     ; Display the intro string
           mov  AX,[VidBufSize] ; AX gets the value to convert to a string
           mov  SI,DigitStr   ; String equivalent is written to DigitStr
           call Word2Str      ; Do the actual string conversion
           PokeChar DigitStr,'H',4   ; Append 'H' on the end of the string
           Writeln DigitStr,5 ;  and display the string equivalent
           Newline

           mov  AH,4CH        ; Terminate process DOS service
           mov  AL,0          ; Pass this value back to ERRORLEVEL
           int  21H           ; Control returns to DOS


;---------------------------------------------------------------
;   Byte2Str  --  Converts a byte passed in AL to a string at
;                 DS:SI
;   Last update 9/18/99
;
;   1 entry point:
;
;   Byte2Str:
;      Caller must pass:
;      AL : Byte to be converted
;      DS : Segment of destination string
;      SI : Offset of destination string
;
;      This routine converts 8-bit values to 2-digit hexadecimal
;      string representations at DS:SI.
;---------------------------------------------------------------

Byte2Str:
       mov DI,AX                ; Duplicate byte in DI
       and DI,000FH             ; Mask out high 12 bits of DI
       mov BX,Digits     ; Load offset of Digits into DI
       mov AH,BYTE [BX+DI]  ; Load digit from table into AH
       mov [SI+1],AH            ;   and store digit into string
       xor AH,AH                ; Zero out AH
       mov DI,AX                ; And move byte into DI
;       shr DI,4   ; This can do the four shifts on 286 and later
       shr DI,1                 ; Shift high nybble of byte to
       shr DI,1                 ;   low nybble
       shr DI,1
       shr DI,1
       mov AH,BYTE [BX+DI]  ; Load digit from table into AH
       mov [SI],AH              ;   and store digit into string
       ret                      ; We're done--go home!


;---------------------------------------------------------------
;   Word2Str  --  Converts a word passed in AX to a string at
;                 DS:SI
;   Last update 9/19/99
;
;   1 entry point:
;
;   Word2Str:
;      Caller must pass:
;      AX : Word value (16 bits) to be converted
;      DS : Segment of destination string
;      SI : Offset of destination string
;---------------------------------------------------------------

Word2Str:
       mov  CX,AX       ; Save a copy of convertee in CX
       xchg AH,AL       ; Swap high and low AX bytes to do high first
       call Byte2Str    ; Convert AL to string at DS:SI
       add  SI,2        ; Bump SI to point to second 2 characters
       mov  AX,CX       ; Reload convertee into AX
       call Byte2Str    ; Convert AL to string at DS:SI
       ret              ; And we're done!


;---------------------------------------------------------------
;   VidCheck  --  Identifies display board & display parameters
;   Last update 9/18/99
;
;   1 entry point:
;
;   VidCheck:
;      Caller need pass no parameters.
;      VidCheck identifies the installed display board by
;      calling DispID.  It then calculates numerous display
;      information values, which it then stores in the block
;      of display information variables in the data segment.
;---------------------------------------------------------------

VidCheck:
        ; First task is to figure out which board is on the bus:
        call DispID        ; Ask BIOS for adapter code; returns in AL
        mov  [DispType],AL ; Store display adapter code in DispType

        ; Next we determine the font size currently in force:
        cmp  AL,0AH        ; See if board is an MCGA
        jl   TryOld        ; If less than code 0AH, it's not an MCGA
        mov  [FontSize],BYTE 16 ; MCGA supports *only* 16 pixel text font
        jmp  GetName       ; Jump ahead to look up adapter name string
TryOld: cmp  [DispType],BYTE 01 ; Is the display adapter code 1, for MDA?
        jne  TryCGA        ; If not, go test for CGA code 2
        mov  [FontSize],BYTE 14 ; MDA uses *only* 14-pixel text font
        jmp  GetName       ; Jump ahead to look up adapter name string
TryCGA: cmp  [DispType],BYTE 02 ; Is the display adapter code 2, for CGA?
        jne  TryVGA        ; If not, go test for EGA/VGA font size
        mov  [FontSize],BYTE 08 ; CGA uses *only* 8-pixel text font
        jmp  GetName       ; Jump ahead to look up adapter name string
TryVGA: mov  AH,11H        ; Select VIDEO Get Font Information subservice
        mov  AL,30H        ;   requires AH = 11H and AL = 30H
        mov  BH,0          ; 0 = Get info about current font
        int  10H           ; Call VIDEO
        mov  [FontSize],CL ; Font size in pixels is returned in CL

        ; Next we get the name string for the board from the info table:
GetName:
        mov  AL,[DispType] ; Load display adapter code into AL
        xor  AH,AH         ; Zero AH so we don't copy trash into DI
        mov  DI,AX         ; Copy AX (with code in AL) into DI
        mov  CL,5          ; We must shift the code 5 bits to mult. by 32
        shl  DI,CL         ; Multiply code by 32 to act as table index
        mov  BX,VidInfoTbl ; Load address of origin table into BX
        mov  [BordName],BX ; Save pointer to video info. table in BordName
        add  [BordName],DI ; Add offset into table to right element

        ; Next we get the refresh buffer segment from the table:
        mov  AX,[BX+DI+27] ; Index into table past name string to segment
        mov  [VidSegment],AX ; Store segment from table to VidSegment variable

        ; Here we calculate the number of lines on-screen from font size:
        xor  AH,AH         ; Make sure AH has no trash in it
        mov  AL,[FontSize] ; Load the font size in pixels into AL
        cmp  AL,8          ; Is it the 8-pixel font?
        jne  Try14         ; If not, try the 14-pixel font
        mov  AL,1          ; The 8-pixel font is table offset 1
        jmp  ReadLns       ; Jump ahead to read screen lines from table
Try14:  cmp  AL,14         ; Is it the 14-pixel font?
        jne  Do16          ; If not, it has to be the 16-pixel font
        mov  AL,2          ; The 14-pixel font is table offset 2
        jmp  ReadLns       ; Jump ahead to read screen lines from table
Do16:   mov  AL,3          ; The 16-pixel font is table offset 3
ReadLns:
        add  DI,AX         ; Add font size offset to table element offset
        mov  AL,[BX+DI+28] ; Load the screen lines value from the table
        mov  [VisibleY],AL ;  and store it in the VisibleY variable
        mov  AH,[VisibleX] ; Load the screen columns value to AH
        xchg AH,AL         ; Exchange AH & AL for 0-basing
        dec  AL            ; Subtract one from column count for 0-basing
        dec  AH            ; Subtract one from line count for zero-basing
        mov  [LRXY],AX       ; And store 0-based X,Y word into LRXY variable

        ; Finally, we calculate the size of the refresh buffer in bytes:
        mov  AL,[VisibleY] ; We multiply screen lines time screen columns
        mul  BYTE [VisibleX] ;  times 2 (for attributes) to get buffer size
        shl  AX,1          ; Multiply lines * columns by 2
        mov  [VidBufSize],AX ; Store refresh buffer size in VidBufSize

        ret                ; Return to caller


;---------------------------------------------------------------
;   DispID  --  Identifies the installed display adapter
;   Last update 9/18/99
;
;   1 entry point:
;
;   DispID:
;      Caller passes no parameters
;      Routine returns a code value in AX.
;      The codes are these:
;      0 : Adapter is unknown; recommend aborting
;      1 : MDA (Monochrome Display Adapter)
;      2 : CGA (Color Graphics Adapter)
;
;---------------------------------------------------------------

DispID:
        mov  AH,1AH      ; Select PS/2 Identify Adapter Service
        xor  AL,AL       ; Select Get Combination Code Subservice (AL=0)
        int  10H         ; Call VIDEO
        cmp  AL,1AH      ; If AL comes back with 1AH, we have a PS/2
        jne  TryEGA      ; If not, jump down to test for the EGA
        mov  AL,BL       ; Put Combination Code into AL
        ret              ;   and go home!
TryEGA: mov  AH,12H      ; Select EGA Alternate Function
        mov  BX,10H      ; Select Get Configuration Information subservice
        int  10H         ; Call VIDEO
        cmp  BX,10H      ; If BX comes back unchanged, EGA is *not* there
        je   OldBords    ; Go see whether it's an MDA or CGA
        cmp  BH,0        ; If BH = 0, it's an EGA/color combo
        je   EGAColor    ;   otherwise it's EGA/mono
        mov  AL,5        ; Store code 5 for EGA mono
        ret              ;   and go home!
EGAColor:
        mov  AL,4        ; Store code 4 for EGA color
        ret              ;   and go home!
OldBords:
        int  11H         ; Call Equipment Configuration interrupt
        and  AL,30H      ; Mask out all but bits 4 & 5
        cmp  AL,30H      ; If bits 4 & 5 are both =1, it's an MDA
        jne  CGA         ;   otherwise it's a CGA
        mov  AL,1        ; Store code 1 for MDA
        ret              ;   and go home!
CGA:    mov  AL,2        ; Store code 2 for CGA
        ret              ;   and go home!


[SECTION .data]

;---------------------------------------------------------------
; DISPLAY INFORMATION VARIABLES
;
; The following block of variables all relate to the video
; system and are initialized by the VidCheck procedure:
;---------------------------------------------------------------
DispType   DB      0       ; Code for display adapter type
VidSegment DW      0B000H  ; Segment of installed display buffer
VidOrigin  DW      0       ; Offset for FAR pointer to refresh buffer
VisibleX   DB      80      ; Number of columns on screen
VisibleY   DB      25      ; Number of lines on screen
VidBufSize DW      4000    ; Default to 25 X 80 X 2 (char & attribute)
FontSize   DB      8       ; Either 8, 14, or 16; default to 8
BordName   DW      0       ; NEAR pointer to name string of installed board
; 18H = 24D; 4FH = 79D; Combined 0-based X,Y of 80 x 25 screen LR corner:
LRXY       DW      184FH

;---------------------------------------------------------------
; DISPLAY ADAPTER INFORMATION LOOKUP TABLE
;
; This is the lookup table containing information on all legal
; display adapters.  The first field in each element is a 26-
; character string containing a brief description of the
; adapter.  The next field is the segment of the video refresh
; buffer.  The last three fields are the number of screen lines
; an adapter displays when the 8-pixel, 14-pixel, and 16-pixel
; fonts are loaded, respectively.  Note that not all adapters
; support all fonts, but a screen line count is given for all
; three fonts for all adapter types.  Illegal combinations will
; not be accessed.
;---------------------------------------------------------------
VidInfoTbl DB      'No adapter identified      '    ; Code 0
           DW      0B000H
           DB      25,25,25
           DB      'Monochrome Display Adapter '    ; Code 1
           DW      0B000H
           DB      25,25,25
           DB      'Color Graphics Adapter     '    ; Code 2
           DW      0B800H
           DB      25,25,25
           DB      'Code 3: Undefined          '    ; Code 3
           DW      0B000H
           DB      25,25,25
           DB      'EGA with color monitor     '    ; Code 4
           DW      0B800H
           DB      43,25,25
           DB      'EGA with mono monitor      '    ; Code 5
           DW      0B000H
           DB      43,25,25
           DB      'Code 6: Undefined          '    ; Code 6
           DW      0B000H
           DB      25,25,25
           DB      'VGA with mono monitor      '    ; Code 7
           DW      0B000H
           DB      50,27,25
           DB      'VGA with color monitor     '    ; Code 8
           DW      0B800H
           DB      50,27,25
           DB      'Code 9: Undefined          '    ; Code 9
           DW      0B000H
           DB      25,25,25
           DB      'MCGA with digital color    '    ; Code 0AH
           DW      0B800H
           DB      25,25,25
           DB      'MCGA with monochrome       '    ; Code 0BH
           DW      0B000H
           DB      25,25,25
           DB      'MCGA with analog color     '    ; Code 0CH
           DW      0B800H
           DB      25,25,25

Digits     DB      '0123456789ABCDEF' ; Lookup table for numeric/string conv.

;---------------------------------------------------------------
; These two variables are screen-clear "atoms" useable by the
; Clear macro.  The high byte is the display attribute, while
; the low byte is the character with which Clear fills the
; video refresh buffer to clear the screen.
;---------------------------------------------------------------
HToneAtom  DW      07B0H          ; Clears screen to halftone pattern
ClearAtom  DW      0720H          ; Clears screen to blanks

;---------------------------------------------------------------
; This is where all predefined string variables are stored.
;---------------------------------------------------------------
CRLF       DB      0DH,0AH        ; Newline string
IDString   DB      '>>>INFO V2.0'
LIDString  EQU     $-IDString
AuthorStr  DB      '   by Jeff Duntemann K7JPD'
LAuthorStr EQU     $-AuthorStr
VidIDStr   DB      '   The installed video board is: '
LVidIDStr  EQU     $-VidIDStr
OrgIDStr   DB      '   The segment of the video refresh buffer is: '
LOrgIDStr  EQU     $-OrgIDStr
FontSzStr  DB      '   The size of the current text font is: '
LFontSzStr EQU     $-FontSzStr
ScrnLnStr  DB      '   The number of lines currently on the screen is: '
LScrnLnStr EQU     $-ScrnLnStr
BufSizStr  DB      '   The size of the refresh buffer in bytes is: '
LBufSizStr EQU     $-BufSizStr
DigitStr   DB      '       '
LDigitStr  EQU     $-DigitStr