;****************************************************************************
;*
;*                        The Universal VESA VBE
;*
;*                  Copyright (C) 1993 Kendall Bennett.
;*                          All rights reserved.
;*
;* Filename:    $RCSfile: vesavbe.asm $
;* Version:     $Revision: 1.3 $
;*
;* Language:    8086 Assembler
;* Environment: IBM PC (MS DOS)
;*
;* Description: This source file contains the code for a Universal VESA VBE
;*              TSR, to implement the VESA Video BIOS Extensions (VBE) for
;*              supported SuperVGA's.
;*
;*              Note that this VESA VBE will also support bank switching
;*              and page flipping for the _standard_ VGA video modes, as
;*              well as the SuperVGA modes.
;*
;*              Must be assembled in the large memory model.
;*
;* $Id: vesavbe.asm 1.3 1993/09/24 05:22:51 kjb release $
;*
;* Revision History:
;* -----------------
;*
;* $Log: vesavbe.asm $
;* Revision 1.3  1993/09/24  05:22:51  kjb
;* Fixed a number of bugs.
;*
;* Revision 1.2  1993/09/23  03:30:25  kjb
;* Fixed bugs with Paradise, Trident, S3 and others...
;*
;* Revision 1.1  1993/09/19  01:26:40  kjb
;* Initial revision
;*
;****************************************************************************

        IDEAL
        DOSSEG
        JUMPS

INCLUDE "MODEL.MAC"                 ; Memory model macros

header  main                        ; Link with main.c

;----------------------------------------------------------------------------
;       Internal Equates and Macros
;----------------------------------------------------------------------------

INCLUDE "CHIPSETS.EQU"              ; Include Equates for Mgraph Routines

CRTC            EQU 3D4h            ; Port of CRTC registers
VGABufferSeg    EQU 0A000h          ; Segment of VGA display memory
NL              EQU <0dh, 0ah>      ; New Line marker
EOS             EQU '$'             ; End of String marker
PSPSize         EQU 100h            ; Size of program PSP
SUCCESS         EQU 004Fh           ; Return value on success
FAILURE         EQU 014Fh           ; Return value on failure
VBE_FUNCTIONS   EQU 04Fh            ; VBE Control function value (AH)
MAX_FUNCTION    EQU 8               ; Highest VBE function number supported
VBE_VERSION     EQU 0102h           ; Version 1.2
NOT_VALID       EQU 0FFFFh          ; Flags function is not valid
WRITE_BUF_SIZE  EQU 058h            ; Size of buffer for write bank routine
READ_BUF_SIZE   EQU 025h            ; Size of buffer for read bank routine
PAGE_BUF_SIZE   EQU 053h            ; Size of buffer for paging routine
SETUP_BUF_SIZE  EQU 046h            ; Size of buffer for SuperVGA setup
EXIT_BUF_SIZE   EQU 01Eh            ; Size of buffer for SuperVGA exit
DAC_BUF_SIZE    EQU 01h             ; Size of buffer for DAC routines

; Macro to get the value of an interrupt vector. Returns the result in
; ES:BX.

MACRO   GetVector   intnum
        mov     ah,35h
        mov     al,intnum
        int     21h
ENDM

; Macro to set the value of an interrupt vector.

MACRO   SetVector   intnum, handler
        push    ds
        mov     dx,seg handler
        mov     ds,dx
        mov     dx,offset handler
        mov     ah,25h
        mov     al,intnum
        int     21h
        pop     ds
ENDM

; Macro to simulate and Int 10h to the old VIDEO BIOS. We need to ensure
; mutually exclusive access to our VBE routines, so we need to modify
; a flag that to let us know if the underlying BIOS has made a direct
; call to us, and pass this call back to the BIOS.

MACRO   CallOldBIOS
        inc     [InsideVBE]         ; Ensure mutal exclusion!
        pushf
        call    [OldInt10]          ; Call the old BIOS
        dec     [InsideVBE]
ENDM

; Macro to set the low start address bits, and to load BX with the
; top bits for SuperVGA adapters.

MACRO   SetLowStartAddress
        mov     dx,03D4h            ; DX := CRTC I/O port (3D4h)
        mov     ax,bx
        out     dx,ax
        mov     ax,cx
        out     dx,ax
        mov     bx,si               ; BX := Bits 16+ for start address
ENDM

; Macro to set the low start address bits, and to load BX with the
; top bits for SuperVGA adapters. This version is for modes where the
; start address value is in 8 byte increments rather than 4 byte increments
; in 256 color modes, so we must shift the start address right 1 bit.

MACRO   SetLowStartAddress256
		mov		al,bl				; Save value of BL
		mov		bl,bh
		mov		bh,ch				; BX := start address bits 15-0
		shr		si,1
		rcr		bx,1				; Shift entire address right 1 bit
		mov		ch,bh				; CH := Bits 15-8 of start address
		mov		bh,bl				; BH := Bits 7-0 of start address
		mov		bl,al				; BL := index of start address low register
		mov     dx,03D4h            ; DX := CRTC I/O port (3D4h)
        mov     ax,bx
        out     dx,ax
        mov     ax,cx
        out     dx,ax
        mov     bx,si               ; BX := Bits 16+ for start address
ENDM

; Macros to do simplified port I/O. Don't use these were speed is of
; the essence, but only as a convenience.

MACRO   inp     port
        mov     dx,port             ;; Read a byte from port 'port' into al
        in      al,dx
ENDM

MACRO   outp    port, value
        mov     dx,port             ;; Write a byte to port 'port'
        mov     al,value
        out     dx,al
ENDM

MACRO   rdinx   port, index
        mov     dx,port             ;; Read register 'port' index 'index'
        mov     al,index
        out     dx,al
        inc     dx
        in      al,dx               ;; Result in AL
ENDM

MACRO   wrinx   port, index, val
        mov     al,index            ;; Write 'port' index 'index' with 'val'
        mov     ah,val
        mov     dx,port
        out     dx,ax
ENDM

MACRO   modinx  port, index, mask, nvw
        mov     dx,port
        mov     al,index
        out     dx,al
        inc     dx
        in      al,dx               ;; Read the old value
        and     al,not mask         ;; Mask out bits to be changed
        or      al,nvw              ;; Or in the changed bits
        out     dx,al               ;; Write the value back again
ENDM

MACRO	dacToPel
		mov		dx,3C8h
		in		al,dx
ENDM

MACRO	dacToCommand
		mov		dx,3C8h
		in		al,dx
		mov		dx,3C6h
		in		al,dx
		in		al,dx
		in		al,dx
		in		al,dx
ENDM

begcodeseg  test

        EXTRN   _testISR:FAR

endcodeseg  test

begcodeseg  main

        EXTRN   __psp:WORD                  ; Segment of program PSP

;----------------------------------------------------------------------------
;       Resident Data
;----------------------------------------------------------------------------

; Signature used to determine if the TSR is already installed.

Signature:          db  'Universal VESA VBE 4.0',0
SigLen              =   $-Signature
LABEL               OldInt10    DWORD
OldInt10off         dw  ?                   ; Place to save int 10h vector
OldInt10seg         dw  ?
InsideVBE           dw  false               ; True if inside VBE ISR routine
VBECompliant        dw  ?                   ; Version if BIOS is VBE already

; Globals used by resident driver

CntSuperVGA         dw  0                   ; Installed SuperVGA
CntChipID           dw  0                   ; Graphics driver chip ID
CntMemory           dw  0                   ; Amount of memory on chip
CntDAC              dw  0                   ; Video DAC ID
CntColors           dw  0                   ; Current color mode
CntMode             dw  0                   ; Current VBE mode number
CurBytesPerLine     dw  0                   ; Current bytes per line value
CntStartX           dw  0                   ; Current display start X
CntStartY           dw  0                   ; Current display start Y
CntDacWidth         db  6                   ; Current DAC width
CurBank             dw  0                   ; Current window bank number

; Vectors to routines currently in use. The information within the PMInfo
; block can be obtained upon special request by application programs
; via the GetModeInfo routine with the mode number set to -1. The routines
; may then be relocated into the programs own address space for faster
; execution in protected mode (and also real mode). Please consult the
; programming information guide about this, as the interface to these
; routines is different to the VESA VBE routines.

TwoBanks        dw  false       ; Flags if two banks available

PMInfo:
WriteBankLen    dw  ?           ; Relocatable length of write bank routine
WriteBank       dw  NOT_VALID   ; Address of current write bank routine
				dw  seg NewPage16Buf
ReadBankLen     dw  ?           ; Relocatable lenth of read bank routine
ReadBank        dw  NOT_VALID   ; Address of current read bank routine
				dw  seg NewPage16Buf
NewPageLen      dw  ?           ; Relocatable size of page routine
NewPage         dw  NOT_VALID   ; Address of page flipping routine
				dw  seg NewPage16Buf
PMInfoSize      =   ($-PMInfo)

; Vectors to all routines used by the resident driver.

WriteBank16     dw  NOT_VALID   ; Address of 16 color write bank routine
WriteBank16Len  dw  0           ; Size of 16 color write bank routine
ReadBank16      dw  NOT_VALID   ; Address of 16 color read bank routine
ReadBank16Len   dw  0           ; Size of 16 color read bank routine
WriteBank256    dw  NOT_VALID   ; Address of 256 color write bank routine
WriteBank256Len dw  0           ; Size of 256 color write bank routine
ReadBank256     dw  NOT_VALID   ; Address of 256 color read bank routine
ReadBank256Len  dw  0           ; Size of 256 color read bank routine
NewPage16		dw  NOT_VALID   ; Address of 16 color page flipping routine
NewPage16Len    dw  0           ; Relocatable size of page routine
NewPage256		dw  NOT_VALID   ; Address of 256 color page flipping routine
NewPage256Len	dw  0           ; Relocatable size of page routine
SetupSVGA       dw  NOT_VALID   ; Address of SuperVGA setup routine
SetupSVGALen    dw  0           ; Size of SuperVGA setup routine
ExitSVGA        dw  NOT_VALID   ; Address of SuperVGA exit routine
ExitSVGALen     dw  0           ; Size of SuperVGA exit routine
Set8BitDac      dw  NOT_VALID   ; Set the DAC to 8 bits wide
Set8BitDacLen   dw  0           ; Size of DAC routine
Set6BitDac      dw  NOT_VALID   ; Set the DAC to 6 bits wide
Set6BitDacLen   dw  0           ; Size of DAC routine

; Dispatch table for supported function calls

DispatchTable       dw  offset SuperVGAInfo
                    dw  seg    SuperVGAInfo
                    dw  offset SuperVGAModeInfo
                    dw  seg    SuperVGAModeInfo
                    dw  offset SuperVGASetMode
                    dw  seg    SuperVGASetMode
                    dw  offset SuperVGAGetMode
                    dw  seg    SuperVGAGetMode
                    dw  offset SuperVGAVideoState
                    dw  seg    SuperVGAVideoState
                    dw  offset SuperVGABankSwitch
                    dw  seg    SuperVGABankSwitch
                    dw  offset SuperVGAScanlineLength
                    dw  seg    SuperVGAScanlineLength
                    dw  offset SuperVGADisplayStart
                    dw  seg    SuperVGADisplayStart
                    dw  offset SuperVGADACControl
                    dw  seg    SuperVGADaCControl

DispatchFunc        dd  ?

; VGA Information block returned to the user program

VGAInfo             db  'VESA'              ; Signature value
                    dw  VBE_VERSION         ; VBE Version number
                    dw  offset Signature    ; OEM Name offset
                    dw  seg Signature       ; OEM Name segment
                    db  4 DUP (0)           ; Capabilities bits
                    dw  offset ModeTable    ; Video mode table offset
                    dw  seg ModeTable       ; Video mode table segment
VBEMemSize          db  0                   ; Memory size in 64k lots
VGAInfoSize         =   ($-VGAInfo)

; Video mode information block filled in a passed to the user application.
; BIOS output is generally supported by most video modes, but we can never
; be sure so we turn this off for all modes.

ModeInfo            dw  011011b             ; Mode Attributes
WinAAttr            db  0111b               ; Window A attributes
WinBAttr            db  0011b               ; Window B attributes
                    dw  64                  ; Window granularity (in K)
                    dw  64                  ; Window size (in K)
                    dw  VGABufferSeg        ; Start segment of Window A
                    dw  VGABufferSeg        ; Start segment of Window B
                    dw  offset SuperVGABankSwitch
                    dw  seg SuperVGABankSwitch
BytesPerLine        dw  0                   ; Bytes per logical scanline
XRes                dw  0                   ; X resolution
YRes                dw  0                   ; Y resolution
CWidth              db  0                   ; Character width
CHeight             db  0                   ; Character height
Planes              db  0                   ; Number of planes
BitsPerPixel        db  0                   ; Bits per pixel
Banks               db  0                   ; Number of banks
MemMode             db  0                   ; Memory mode
BankSize            db  0                   ; Bank size
Pages               db  0                   ; Number of image pages
                    db  1                   ; Reserved
RedMaskSize         db  0                   ; Red mask size
RedMaskPos          db  0                   ; Red mask position
GreenMaskSize       db  0                   ; Green mask size
GreenMaskPos        db  0                   ; Green mask pos
BlueMaskSize        db  0                   ; Blue mask size
BlueMaskPos         db  0                   ; Blue mask position
ResMaskSize         db  0                   ; Reserved mask size
ResMaskPos          db  0                   ; Reserved mask pos
DirectInfo          db  0                   ; Direct screen mode info
ModeInfoSize        =   ($-ModeInfo)

; Table to construct the mask position information for direct video
; modes.

maskDefInfo         db  00h,00h,00h,00h,00h,00h,00h,00h,00h
mask32kInfo         db  05h,0Ah,05h,05h,05h,00h,01h,0Bh,02h
mask64kInfo         db  05h,0Bh,06h,05h,05h,00h,00h,00h,00h
mask16mInfo         db  08h,10h,08h,08h,08h,00h,00h,00h,00h
maskInfoSize        =   ($-mask16mInfo)

; List of all possible supported VBE video modes. This will be filled
; in by the high level language code during the detection phase.

ModeTable           dw  31 dup (?)

; Index Table for filling in the above mode information table and for
; finding the values to use to initialise a video mode via int 10h. This
; table is constructed and copied here by the high level code. Refer
; to the 'detect.c' file for information on it's structure.

INDEX_ENTRY_SIZE    =   20
PARTIAL_INDEX_SIZE  =   14

IndexTable          db  (31 * INDEX_ENTRY_SIZE) dup (?)

;----------------------------------------------------------------------------
;       Resident Code
;----------------------------------------------------------------------------

        ASSUME  cs:main_TEXT, ds:NOTHING, es:NOTHING

;----------------------------------------------------------------------------
; Int10ISR  - Video BIOS hook.
;----------------------------------------------------------------------------
;
; This routine is called whenever an application program makes a call to
; the Video BIOS routines. We check to see if the calls and VBE functions
; calls, and handle them appropriately, or else we simply pass the call
; to the old Video BIOS handler.
;
;----------------------------------------------------------------------------
PROC    Int10ISR    far

; Some BIOSes directly call themselves to get things done, which can cause
; the ISR routines to get into an infinite loop. Thus we have a special flag
; to tell us if we are already in the ISR at level one, and to simply call
; the underlying VIDEO BIOS directly rather than processing the call
; ourselves since we made the call anyway :-)

        sti
        cmp     [InsideVBE],0
        jne     @@ToOldBIOS

        cmp     ah,VBE_FUNCTIONS    ; Is this a VBE function?
        je      @@VBEFunction       ; Yes, so handle it

; Check to see if this is the Set Video Mode function, in which case we
; need to check if we are trying to set a standard VGA video mode.

        or      ah,ah
        jnz     @@ToOldBIOS
        cmp     al,0Dh
        jb      @@ToOldBIOS
        cmp     al,13h
        ja      @@ToOldBIOS

; Attempting to set a standard VGA video mode, so pass this call onto the
; SuperVGASetMode routine.

        mov     bx,ax               ; BX := video mode number
        push    si                  ; Save registers
        push    di
        push    ds
        push    es
        call    SuperVGASetMode     ; Set the video mode
        pop     es
        pop     ds                  ; Restore value of registers
        pop     di
        pop     si
        iret

@@ToOldBIOS:
        jmp     [OldInt10]          ; Jump to old VIDEO BIOS

@@VBEFunction:
        cmp     al,MAX_FUNCTION
        ja      @@ToOldBIOS         ; Not one of ours, so pass it on

; This is a VBE function, so dispatch to the appropriate routine depending
; on the subfunction number.

        xor     ah,ah
        shl     al,2                ; AX := index into table
        push    bx                  ; Save value of BX
        mov     bx,ax
        mov     ax,[DispatchTable + bx]
        mov     [WORD DispatchFunc],ax
        mov     ax,[DispatchTable + bx + 2]
        mov     [WORD DispatchFunc+2],ax
        pop     bx                  ; Restore BX
        call    [DispatchFunc]      ; Call the appropriate function
        iret

ENDP    Int10ISR

;----------------------------------------------------------------------------
; SuperVGAInfo  - Get SuperVGA Information
;----------------------------------------------------------------------------
;
; Function to return information about the SuperVGA video card.
;
; Entry:        ES:DI   -> 256byte buffer for SuperVGA information
;
; Exit:         AX      - VBE status code
;
;----------------------------------------------------------------------------
PROC    SuperVGAInfo    far

        push    si                  ; Save registers
        push    di
        push    ds
        push    es

        mov     ax,cs
        mov     ds,ax
        mov     si,offset VGAInfo   ; DS:SI -> VGAInfo Table
        mov     cx,VGAInfoSize
        cld
    rep movsb                       ; Copy the data across
        xor     ax,ax
        mov     cx,256-VGAInfoSize
    rep stosb                       ; Clear out remaining buffer
        mov     ax,SUCCESS          ; Return status code

        pop     es
        pop     ds                  ; Restore value of registers
        pop     di
        pop     si
        ret

ENDP    SuperVGAInfo

;----------------------------------------------------------------------------
; SuperVGAModeInfo  - Get SuperVGA Video Mode Information
;----------------------------------------------------------------------------
;
; Function to return information about a specific VBE SuperVGA video mode.
;
; If CX := -1, we return information about how to access the relocatable
; bank switching and page flipping routines.
;
; Entry:        CX      - SuperVGA video mode
;               ES:DI   -> 256 byte buffer for mode information
;
; Exit:         AX      - VBE status code
;
;----------------------------------------------------------------------------
PROC    SuperVGAModeInfo    far

        push    si                  ; Save registers
        push    di
        push    ds
        push    es

        cmp     cx,-1               ; Our special function call?
        je      @@DoSpecial         ; Yes, handle it...

; Determine if the video mode is one of our valid video modes.

        call    ValidateVBEMode     ; DS:SI -> Index Table entry
        cmp     si,-1
        je      @@Invalid           ; Video mode is invalid to us!!

; Copy the information specific to the video mode into buffer

        push    es
        push    di                  ; Save position of user buffer
        add     si,2                ; Increment to start of mode info
        push    cs
        pop     es
        mov     di,offset BytesPerLine  ; ES:DI -> Destination buffer
        mov     cx,PARTIAL_INDEX_SIZE
        cld
    rep movsb                       ; Copy the data across

; Now copy the mask information across. This will be cleared for non
; direct color modes.

        mov     cx,maskInfoSize
        mov     di,offset RedMaskSize
        mov     si,offset maskDefInfo
        mov     dl,[BitsPerPixel]
        cmp     dl,15               ; 32k color mode?
        jl      @@CopyMask          ; No, must be 16 or 256
        mov     si,offset mask32kInfo
        cmp     dl,16               ; 64k color mode?
        jl      @@CopyMask          ; No, must be 32k color mode
        mov     si,offset mask64kInfo
        cmp     dl,24               ; 16m color mode?
        jl      @@CopyMask          ; No, must be 64k color mode
        mov     si,offset mask16mInfo

@@CopyMask:
    rep movsb                       ; Copy mask information across

; Copy the mode information across into application buffer

        pop     di
        pop     es                  ; ES:DI -> User mode info buffer
        mov     si,offset ModeInfo  ; DS:SI -> ModeInfo Table
        mov     cx,ModeInfoSize
    rep movsb                       ; Copy the data across
        xor     ax,ax
        mov     cx,256-ModeInfoSize
    rep stosb                       ; Clear out remaining buffer

@@Exit:
        pop     es
        pop     ds                  ; Restore value of registers
        pop     di
        pop     si
        mov     ax,SUCCESS
        ret

@@DoSpecial:
        push    cs
        pop     ds
        mov     si,offset PMInfo    ; DS:SI -> Protected mode information
        mov     cx,PMInfoSize
    rep movsb                       ; Copy the information across...
        mov     cx,0CABDh           ; Flag that we are here!!
        jmp     @@Exit

; The video mode is invalid, but the underlying video BIOS may be VBE
; compatible, and the mode number may mean something to it, so we pass it
; on to it.

@@Invalid:
        pop     es
        pop     ds                  ; Restore value of registers
        pop     di
        pop     si
        mov     ax,4F01h
        CallOldBIOS
        ret

ENDP    SuperVGAModeInfo

;----------------------------------------------------------------------------
; SuperVGASetMode   - Set SuperVGA Video Mode
;----------------------------------------------------------------------------
;
; Function to set a specific VBE SuperVGA video mode. Only graphics modes
; are supported by this TSR. Text modes are passed to an underlying VBE
; if one is present.
;
; Not that we also handle the setting of standard video modes in this
; routine, since we need to be able to setup the correct bank switching and
; paging routines to work with this video mode.
;
; Entry:        BX  - VBE video mode number (bit 15 means don't clear mem).
;
; Exit:         AX  - VBE status code.
;
;----------------------------------------------------------------------------
PROC    SuperVGASetMode     far

        push    si                  ; Save registers
        push    di
        push    ds
        push    es

        mov     cx,bx
        and     cl,7Fh
        cmp     cx,13h
        ja      @@VBEMode

; Attempting to set a standard VGA video mode, so save the current
; color mode for the mode and get the BIOS to initialise it. Note that we
; call the SuperVGA exit routine first to bring the SuperVGA back into
; it's default state. We also reset the DAC back to normal DAC mode
; in case the BIOS does not do this correctly.

		cmp     [ExitSVGA],NOT_VALID
		je      @@1
		call    [ExitSVGA]              ; Call the SuperVGA exit code
@@1:	dacToCommand
		xor		al,al
		out		dx,al					; Reset DAC to normal mode
		dacToPel
		push    cx
        push    bx

        mov     [CntColors],gr16Color   ; Assume 16 color mode
        cmp     cx,13h
        jl      @@2
        mov     [CntColors],gr256Color  ; Nope, must be 256 color mode
@@2:    mov     [CntMode],cx        ; Save the current mode number

        mov     [CurBytesPerLine],40
        cmp     cx,0Dh
        je      @@SetVGAMode
        mov     [CurBytesPerLine],80
        cmp     cx,13h
        jl      @@SetVGAMode
        mov     [CurBytesPerLine],320

@@SetVGAMode:
        pop     ax                  ; AX := mode number to set
        CallOldBIOS                 ; Get BIOS to initialise the mode
        pop     cx                  ; Restore value of VBE mode
        cmp     [CntMode],3
        jl      @@Done
        cmp     [CntMode],13h
        ja      @@Done
        jmp     @@StandardVGAMode

; We have a VBE mode to set. First we need to find the correct mode
; information table entry, and lookup the values we need to get the
; BIOS to initialise the video mode.

@@VBEMode:
        push    bx                  ; Save VBE mode number for later
        mov     cx,bx
        and     ch,07Fh             ; Clear memory clear bit
        call    ValidateVBEMode     ; DS:SI -> Mode information buffer
        cmp     si,-1
        je      @@InvalidMode       ; Video mode is invalid to us!
        mov     [CntMode],cx        ; Save current mode number
        mov     ax,[si+16]          ; AX := AXVal from mode info
        mov     bx,[si+18]          ; BX := BXVal from mode info
        mov     dx,[si+2]           ; DX := BytesPerLine value
        mov     [CurBytesPerLine],dx; Save the BytesPerLine value for mode
        mov     dl,[si+11]          ; DL := BitsPerPixel
        pop     cx                  ; CX := VBE mode number
        and     ch,80h              ; isolate clear memory bit
        jz      @@SetMode           ; Bit is not set, so simply set the mode

; Request was made to _not_ clear the video memory, so attempt to set the
; correct bit for the OEM video modes.

        or      bx,bx               ; BX := 0, so AX := video mode number
        jz      @@SetAX
        or      bh,bh
        jz      @@SetBL

        or      bh,80h              ; Set the memory clear bit
        jmp     @@SetMode

@@SetAX:
        or      al,80h              ; Set the memory clear bit
        jmp     @@SetMode

@@SetBL:
        or      bl,80h              ; Set the memory clear bit

@@SetMode:
        push    dx                  ; Preserve bits per pixel value
        CallOldBIOS                 ; Call the BIOS to initialise the mode
        pop     dx

        mov     ax,40h
        mov     es,ax
        cmp     [BYTE es:49h],3     ; Mode is still text mode, did not set
        jbe     @@ModeSetFailed

; Save the current color mode that we are in for correct bank switching
; and CRT addressing.

        mov     [CntColors],gr16Color
        cmp     dl,4
        je      @@StandardVGAMode
        mov     [CntColors],gr256Color
        cmp     dl,8
        je      @@StandardVGAMode
        mov     [CntColors],gr32KColor
        cmp     dl,15
        je      @@StandardVGAMode
        mov     [CntColors],gr64KColor
        cmp     dl,16
        je      @@StandardVGAMode
        mov     [CntColors],gr16MColor

; Setup the correct vectors to point to the appropriate bank switching
; routine for the current color mode.

@@StandardVGAMode:
        cmp     [SetupSVGA],NOT_VALID
        je      @@SetBanks
        call    [SetupSVGA]         ; Setup the SuperVGA

@@SetBanks:
		push	si
		push	di
        mov     ax,[WriteBank16Len]
        mov     bx,[WriteBank16]
        mov     cx,[ReadBank16Len]
		mov     dx,[ReadBank16]
		mov		si,[NewPage16]
		mov		di,[NewPage16Len]
        cmp     [CntColors],gr16Color
        je      @@SetVectors

        mov     ax,[WriteBank256Len]
        mov     bx,[WriteBank256]
        mov     cx,[ReadBank256Len]
		mov     dx,[ReadBank256]
		cmp		[CntMode],13h
		je		@@SetVectors		; Standard VGA mode, use 16 color version
		mov		si,[NewPage256]
		mov		di,[NewPage256Len]

@@SetVectors:
        mov     [WriteBankLen],ax
        mov     [WriteBank],bx
        mov     [ReadBankLen],cx
		mov     [ReadBank],dx
		mov		[NewPage],si
		mov		[NewPageLen],di
		pop		di
		pop		si

@@Done:
        mov     [CntStartX],0
        mov     [CntStartY],0
        mov     ax,SUCCESS
        jmp     @@Exit

@@ModeSetFailed:
        mov     ax,FAILURE
        jmp     @@Exit

; The mode number was invalid to us, but may make sense to an underlying
; VBE compatible BIOS. Note that we also call the SuperVGA exit routine
; to bring the SuperVGA back into it's default state.

@@InvalidMode:
        cmp     [ExitSVGA],NOT_VALID
        je      @@3
        call    [ExitSVGA]              ; Call the SuperVGA exit code
@@3:    pop     bx
        mov     ax,4F02h
        CallOldBIOS
        mov     [CntMode],-1        ; Flag that mode is unknown to us!

@@Exit:
        pop     es
        pop     ds                  ; Restore value of registers
        pop     di
        pop     si
        ret

ENDP    SuperVGASetMode

;----------------------------------------------------------------------------
; SuperVGAGetMode   - Get Current SuperVGA Video Mode
;----------------------------------------------------------------------------
;
; Function to return the mode number of the current set video mode. If the
; mode is a normal VGA video mode, the normal mode number is returned,
; otherwise the VBE video mode number for the mode is returned.
;
; Exit:         AX  - VBE status code
;               BX  - Current video mode
;
;----------------------------------------------------------------------------
PROC    SuperVGAGetMode     far

        push    si                  ; Save registers
        push    di
        push    ds
        push    es

        mov     ax,40h
        mov     es,ax
        xor     bx,bx
        mov     bl,[BYTE es:49h]
        and     bl,7Fh
        cmp     bl,13h
        mov     ax,SUCCESS
        jbe     @@VGAMode

        mov     bx,[CntMode]        ; VBE Video mode is active, so return it
        cmp     bx,-1
        jne     @@VGAMode

; The mode was not known to us, so it may be an underlying VBE video mode.

        mov     ax,4F03h
        CallOldBIOS

@@VGAMode:
        pop     es
        pop     ds                  ; Restore value of registers
        pop     di
        pop     si
        ret

ENDP    SuperVGAGetMode

;----------------------------------------------------------------------------
; SuperVGAVideoState    - Save/Restore SuperVGA video state
;----------------------------------------------------------------------------
;
; Function to save save/restore the SuperVGA video state. This function
; would be very tricky to implement for all supported SuperVGA's if we
; want to save the entire state. In order to do as best as job as possible,
; we save the state of the hardware as much as possible using the normal
; Video BIOS routines, along with the current video BIOS mode number. To
; restore the state we first reset the current video mode, then call the
; BIOS to reset the hardware state, which should work correctly for most
; SuperVGA situations.
;
; Entry:        DL      - Subfunction number
;                         00h   - Get state buffer size
;                         01h   - Save video state
;                         02h   - Restore video state
;               ES:BX   -> Video buffer to save state in
;               CX      - Flags for what to save
;                         bit 0 - Video hardware state
;                         bit 1 - Video BIOS state
;                         bit 2 - Video DAC state
;                         bit 3 - SuperVGA state
;
; Exit:         AX      - VBE Status code
;               BX      - Number of 64 byte blocks needed to save state
;
;----------------------------------------------------------------------------
PROC    SuperVGAVideoState  far

        push    si                  ; Save registers
        push    di
        push    ds
        push    es

        cmp     [VBECompliant],0
        je      @@NoVBE

; Some VBE BIOS'es are stuffed, and return negative values for the
; state size. We check to see if the BIOS functions correctly here,
; using the standard VGA routine if it doesnt.

        push    es
        push    bx
        push    cx
        push    dx
        xor     dl,dl               ; Get state buffer size service
        mov     ax,4F04h
        CallOldBIOS
        or      bx,bx               ; Set flags before popping regs
        pop     dx
        pop     cx
        pop     bx
        pop     es
        js      @@NoVBE             ; Value was bad, so don't use it...

; Use the underlying VBE BIOS to do this for us, rather than use the
; normal VGA routine which may not save all extended register information.

        mov     ax,4F04h
        CallOldBIOS
        jmp     @@Exit

; Otherwise use the standard VGA save/restore routine as a substitute.

@@NoVBE:
        mov     ah,1Ch              ; Save/Restore video state BIOS call
		mov     al,dl				; AL := subfunction number
		and		cl,07h				; Mask out all but normal VGA bits
        CallOldBIOS
        cmp     al,1Ch
        mov     ax,FAILURE
        jne     @@Exit
        mov     ax,SUCCESS

@@Exit:
        pop     es
        pop     ds                  ; Restore value of registers
        pop     di
        pop     si
        ret

ENDP    SuperVGAVideoState

;----------------------------------------------------------------------------
; SuperVGABankSwitch    - CPU Video Memory Control
;----------------------------------------------------------------------------
;
; Function to switch video banks for the SuperVGA. This function can be
; called as a far function call, rather than through the Int 10h mechanism
; for speed.
;
; Entry:        BH  - Subfunction number
;                     00h - Select video memory window
;                     01h - Get video memory window
;               BL  - Window number
;                     00h - Window A
;                     01h - Window B
;               DX  - Window address in video memory (granularity units)
;
; Exit:         AX  - VBE status code.
;               DX  - Window address in video memory (when requested)
;
;----------------------------------------------------------------------------
PROC    SuperVGABankSwitch  far

        or      bh,bh
        jnz     @@Done              ; Simply return the current window

        mov     ax,dx               ; AX := bank number
        mov     [CurBank],ax        ; Save current bank number
        or      bl,bl
        jnz     @@WindowB
        call    [DWORD WriteBank]   ; Set new write bank
        jmp     @@Done

@@WindowB:
        cmp     [TwoBanks],false
        je      @@Done              ; Dual banking not supported
        call    [DWORD ReadBank]    ; Set new read bank

@@Done:
        mov     dx,[CurBank]        ; Return the current bank number
        mov     ax,SUCCESS
        ret

ENDP    SuperVGABankSwitch

;----------------------------------------------------------------------------
; SuperVGAScanlineWidth - Get/Set Logical Scanline Length
;----------------------------------------------------------------------------
;
; Function to get/set the logical scanline length. This value is specified
; in pixels, but may require rounding to the next available value. The
; actual value set will be returned.
;
; Entry:        BL  - Subfunction number
;                     00h - Set scanline length
;                     01h - Get scanline length
;               CX  - Desired scanline length in pixels
;
; Exit:         AX  - VBE status code
;               BX  - Bytes per scanline
;               CX  - Pixels per scanline
;               DX  - Maximum number of scanlines
;
;----------------------------------------------------------------------------
PROC    SuperVGAScanlineLength  far

        push    si                  ; Save registers
        push    di
        push    ds
        push    es

        mov     ax,[CntColors]
        or      bl,bl
        jnz     @@GetScanlineLength

; We are attempting to set the scanline width for the mode. First we need
; to adjust the value into bytes, and then set the CRTC offset register

        cmp     ax,gr16Color
        je      @@Colors16
        cmp     ax,gr256Color
        je      @@Colors256
        cmp     ax,gr32kColor
        je      @@Colors32k
        cmp     ax,gr64kColor
        je      @@Colors32k

        mov     ax,cx
        shl     cx,1
        add     cx,ax               ; CX := bytes per scanline
        and     cx,0FFF8h           ; Mask off bottom three bits
        mov     [CurBytesPerLine],cx    ; Save BytesPerLine value
        shr     cx,3                ; Shift value for offset register
        jmp     @@SetOffsetReg

@@Colors16:
        shr     cx,3
        and     cx,0FFF0h           ; Mask off bottom four bits
        mov     [CurBytesPerLine],cx    ; Save BytesPerLine value
        shr     cx,4                ; Shift value for offset register
        jmp     @@SetOffsetReg      ; CX := bytes per scanline

@@Colors256:
        and     cx,0FFF8h           ; Mask off bottom three bits
        mov     [CurBytesPerLine],cx    ; Save BytesPerLine value
        shr     cx,3                ; Shift value for offset register
        jmp     @@SetOffsetReg

@@Colors32k:
        shl     cx,1
        and     cx,0FFF8h           ; Mask off bottom three bits
        mov     [CurBytesPerLine],cx    ; Save BytesPerLine value
        shr     cx,3                ; Shift value for offset register

@@SetOffsetReg:
        mov     dx,CRTC
        mov     ah,cl
        mov     al,13h
        out     dx,ax               ; Program the CRTC offset value

; Now return the scanline length. We also compute the maximum number of
; scanlines for the entire amount of display memory installed.

@@GetScanlineLength:
        mov     ax,[CntMemory]      ; AX := amount of memory in k
        mov     dx,ax
        shl     ax,10               ; AX := low order bits of memory
        shr     dx,6                ; DX:AX := memory in bytes
        mov     bx,[CurBytesPerLine]    ; BX := bytes per line
        mov     cx,bx               ; CX := pixel per scanline
        div     bx
        mov     dx,ax               ; DX := number of scanlines
        mov     ax,[CntColors]      ; AX := current color mode
        cmp     ax,gr16Color
        je      @@GetColors16
        cmp     ax,gr256Color
        je      @@Exit
        cmp     ax,gr32kColor
        je      @@GetColors32k
        cmp     ax,gr64kColor
        je      @@GetColors32k

        mov     ax,cx
        shr     cx,1
        sub     cx,ax               ; CX := pixels per scanline
        jmp     @@Exit

@@GetColors16:
        shl     cx,3                ; CX := pixels per scanline
        jmp     @@Exit

@@GetColors32k:
        shr     cx,1                ; CX := pixels per scanline

@@Exit: mov     ax,SUCCESS
        pop     es
        pop     ds                  ; Restore value of registers
        pop     di
        pop     si
        ret

ENDP    SuperVGAScanlineLength

;----------------------------------------------------------------------------
; SuperVGADisplayStart  - Get/Set Display Start
;----------------------------------------------------------------------------
;
; Set the display start address for the current SuperVGA video mode.
;
; Entry:        BH  - 00h
;               BL  - Subfunction number
;                     00h - Set display start
;                     01h - Get display start
;               CX  - Leftmost displayed pixel in scanline
;               DX  - First displayed scanline
;
; Exit:         CX  - Leftmost displayed pixel in scanline
;               DX  - First displayed scanline
;
;----------------------------------------------------------------------------
PROC    SuperVGADisplayStart    far

        mov     ax,FAILURE
        cmp     [NewPage],NOT_VALID
        je      @@Failure

        or      bl,bl
        jnz     @@GetDisplayStart

; Set the starting display address.

        mov     [CntStartX],cx      ; Save the display starting coordinates
        mov     [CntStartY],dx

        mov     ax,[CurBytesPerLine]
        mul     dx                  ; DX:AX := y * bytesPerLine

        mov     bx,[CntColors]
        cmp     bx,gr16Color
        je      @@Colors16
        cmp     bx,gr256Color
        je      @@Colors256
        cmp     bx,gr32kColor
        je      @@Colors32k
        cmp     bx,gr64kColor
        je      @@Colors32k

; Video mode is a 24 bit video mode

        add     ax,cx               ; DX:AX := x + y * bytesPerLine
        adc     dx,0
        shl     cx,1
        add     ax,cx               ; DX:AX := x * 3 + y * bytesPerLine
        adc     dx,0
        shr     dx,1
        rcr     ax,1
        shr     dx,1
        rcr     ax,1                ; Adjust to lie on plane boundary
        jmp     @@SetDisplayStart

; Video mode is a 16 color video mode

@@Colors16:
        shr     cx,3
        add     ax,cx               ; DX:AX := x/8 + y * bytesPerLine
        adc     dx,0
        jmp     @@SetDisplayStart

; Video mode is a 256 color video mode

@@Colors256:
        add     ax,cx               ; DX:AX := x + y * bytesPerLine
        adc     dx,0
        shr     dx,1
        rcr     ax,1
        shr     dx,1
        rcr     ax,1                ; Adjust to lie on plane boundary
        jmp     @@SetDisplayStart

; Video mode is a 32k color video mode

@@Colors32k:
        shl     cx,1
        add     ax,cx               ; DX:AX := x + y * bytesPerLine
        adc     dx,0
        shr     dx,1
        rcr     ax,1
        shr     dx,1
        rcr     ax,1                ; Adjust to lie on plane boundary

@@SetDisplayStart:
        mov     cl,0Ch              ; CL := Start Address High register
        mov     ch,ah               ; CH := high byte of new address
        mov     bh,al               ; BH := low byte of new address
        mov     bl,0Dh              ; BL := Start Address Low register
        mov     si,dx               ; SI := Bits 16+ for SuperVGA's

        call    [DWORD NewPage]     ; Program the start address

        mov     ax,SUCCESS
        xor     bh,bh               ; BH must be zero on exit
        jmp     @@Exit

@@Failure:
        mov     ax,FAILURE
        jmp     @@Exit

@@GetDisplayStart:
        mov     cx,[CntStartX]      ; Return saved starting coordinates
        mov     dx,[CntStartY]
        mov     ax,SUCCESS

@@Exit:
        ret

ENDP    SuperVGADisplayStart

;----------------------------------------------------------------------------
; SuperVGADACControl    - Get/Set video DAC width
;----------------------------------------------------------------------------
;
; Set the display start address for the current SuperVGA video mode.
; Currently we only support changing the DAC size to 8 bits wide, rather
; than the standard 6 bits wide (however, I have yet to find information
; on how to do this for any video cards :-( The framework is here, all I
; need now it to find the information).
;
; Entry:        BH  - Desired number of bits per primary
;               BL  - Subfunction number
;                     00h - Set DAC palette width
;                     01h - Get DAC palette width
;
; Exit:         AX  - VBE status code
;               BH  - Current number of bits per primary
;
;----------------------------------------------------------------------------
PROC    SuperVGADACControl  far

        cmp     [Set8BitDac],NOT_VALID
        jne     @@NoVBE             ; Use our routine if one is supplied
        cmp     [VBECompliant],102h
        jl      @@NoVBE             ; Older BIOS, so must try it ourselves

        mov     ax,4F08h            ; Call the old VBE 1.2 BIOS instead
        CallOldBIOS
        ret

@@NoVBE:
        or      bl,bl
        jnz     @@GetDacWidth

        cmp     [Set8BitDac],NOT_VALID
        je      @@Failure           ; DAC width is fixed...

; Attempting to set the DAC width.

        cmp     bh,6
        je      @@Set6Bit

        call    [Set8BitDac]
        mov     [CntDacWidth],8
        jmp     @@GetDacWidth

@@Set6Bit:
        call    [Set6BitDac]
        mov     [CntDacWidth],6

@@GetDacWidth:
        mov     ax,SUCCESS
        mov     bh,[CntDacWidth]    ; Currently on VGA DAC's are supported
        jmp     @@Exit

@@Failure:
        mov     ax,FAILURE          ; DAC width could not be changed
@@Exit: ret

ENDP    SuperVGADACControl

;----------------------------------------------------------------------------
; ValidateVBEMode   Check if a mode is valid and finds index table entry
;----------------------------------------------------------------------------
;
; Validates that the mode is one of the valid modes for the currently
; installed SuperVGA, and looks up the address of the correct index
; table entry.
;
; Entry:        CX      - VBE mode number
;
; Exit:         CX      - VBE mode number
;               DS:SI   -> Index Table entry for mode
;
; Registers:    none
;
;----------------------------------------------------------------------------
PROC    ValidateVBEMode near

        push    ax
        push    cs
        pop     ds
        mov     si,offset ModeTable ; DS:SI -> Valid modes table

@@ValidLoop:
        lodsw                       ; Get next valid mode
        cmp     ax,-1
        je      @@Invalid           ; Mode is invalid to us!
        cmp     ax,cx
        jne     @@ValidLoop         ; Not this mode, so continue

; Find the index entry in the mode information table for this mode.

        mov     si,offset IndexTable; DS:SI -> Mode Information table

@@ModeLoop:
        cmp     [WORD si],-1
        je      @@Invalid           ; Mode is not in this table
        cmp     [si],cx             ; Is this our mode information block?
        je      @@Done              ; Yes, so we are done
        add     si,INDEX_ENTRY_SIZE
        jmp     @@ModeLoop

@@Invalid:
        mov     si,-1               ; Mode was invalid

@@Done: pop     ax
        ret

ENDP    ValidateVBEMode

;----------------------------------------------------------------------------
; Buffer area for relocated versions of the bank switching and CRT
; addressing routines. The length of the buffer space here must be equal
; to the largest routine, which so far is the S3 bank switching routines.
;----------------------------------------------------------------------------

WriteBank16Buf      db  WRITE_BUF_SIZE dup (0)
ReadBank16Buf       db  READ_BUF_SIZE dup (0)
WriteBank256Buf     db  WRITE_BUF_SIZE dup (0)
ReadBank256Buf      db  READ_BUF_SIZE dup (0)
NewPage16Buf		db  PAGE_BUF_SIZE dup (0)
NewPage256Buf		db  PAGE_BUF_SIZE dup (0)
SetupBuf            db  SETUP_BUF_SIZE dup (0)
ExitBuf             db  EXIT_BUF_SIZE dup (0)
Set8BitDacBuf       db  DAC_BUF_SIZE dup (0)
Set6BitDacBuf       db  DAC_BUF_SIZE dup (0)

EndResident:                        ; End of resident code

;----------------------------------------------------------------------------
;       Transient Code.
;----------------------------------------------------------------------------

        ASSUME  cs:main_TEXT, ds:DGROUP, es:NOTHING

INCLUDE "_ati.asm"
INCLUDE "_ahead.asm"
INCLUDE "_chips.asm"
INCLUDE "_everex.asm"
INCLUDE "_genoa.asm"
INCLUDE "_oak.asm"
INCLUDE "_paradis.asm"
INCLUDE "_trident.asm"
INCLUDE "_video7.asm"
INCLUDE "_tseng.asm"
INCLUDE "_ncr.asm"
INCLUDE "_s3.asm"
INCLUDE "_acumos.asm"
INCLUDE "_avance.asm"
INCLUDE "_mxic.asm"
INCLUDE "_primus.asm"
INCLUDE "_realtek.asm"
INCLUDE "_cirrus.asm"

INCLUDE "_tables.asm"

;----------------------------------------------------------------------------
; bool isLoaded(void)
;----------------------------------------------------------------------------
; Returns true if the Universal VBE TSR is already loaded in memory.
;----------------------------------------------------------------------------
procstart   _isLoaded

        push    ds

        GetVector   10h             ; ES:BX -> Current INT10h vector
        mov     di,bx
        sub     di,offset Int10ISR - offset Signature
                                    ; ES:DI -> Signature in loaded TSR
        push    cs
        pop     ds
        mov     si,offset Signature ; DS:SI -> Signature in .COM file
        mov     cx,SigLen           ; CX := length of signature string
        cld
        mov     ax,1                ; Assume already loaded
        repe    cmpsb
        jcxz    @@AlreadyLoaded     ; Driver is already loaded!

        xor     ax,ax               ; Driver is not loaded

@@AlreadyLoaded:
        pop     ds
        ret

procend     _isLoaded

;----------------------------------------------------------------------------
; bool unload(void)
;----------------------------------------------------------------------------
; Unloads the Universal VESA VBE from memory. This assume it _is_ currently
; installed.
;----------------------------------------------------------------------------
procstart   _unload

        GetVector   10h             ; ES:BX -> Current INT10h vector

; Since we know we are already installed, ES:00 will point to the
; start of our code segment, which is where the signature is located.
; We can thus get the old interrupt vector value which resides in memory
; just after the program signature.

        push    es
        mov     dx,[es:OldInt10Seg]
        mov     ds,dx
        mov     dx,[es:OldInt10Off]
        mov     ax,2510h
        int     21h                 ; Reset the interrupt vector via DOS
        pop     es

; We can find the segment of our PSP by taking 10h of the value of ES, and
; we then pass this value to DOS to free all memory occupied by the TSR
; (note that we freed the environment block when we went TSR).

        mov     ax,es
        sub     ax,10h
        mov     es,ax               ; ES := segment of TSR's PSP
        mov     ah,49h
        int     21h                 ; Free memory associated with TSR

        mov     ax,4C00h
        int     21h                 ; Terminate the program

procend     _unload

;----------------------------------------------------------------------------
; void copyModeInfo(void *IndexTable,int size)
;----------------------------------------------------------------------------
; Copies the mode table information into the resident portion of the
; driver.
;----------------------------------------------------------------------------
procstart   _copyModeInfo

        ARG     pIndexTable:DWORD, tableSize:WORD

        push    bp
        mov     bp,sp
        push    ds
        push    si
        push    di

        mov     ax,[WORD pIndexTable+2]
        mov     ds,ax
        mov     si,[WORD pIndexTable]   ; DS:SI -> source index table
        mov     ax,seg IndexTable
        mov     es,ax
        mov     di,offset IndexTable    ; ES:DI -> destination index table
        mov     cx,[tableSize]
        cld
    rep movsb                           ; Copy the data

        pop     di
        pop     si
        pop     ds
        pop     bp
        ret

procend     _copyModeInfo

;----------------------------------------------------------------------------
; void copyValidModes(void *validModes,int size)
;----------------------------------------------------------------------------
; Copies the valid video modes table into the resident portion of the
; driver.
;----------------------------------------------------------------------------
procstart   _copyValidModes

        ARG     pValidModes:DWORD, tableSize:WORD

        push    bp
        mov     bp,sp
        push    ds
        push    si
        push    di

        mov     ax,[WORD pValidModes+2]
        mov     ds,ax
        mov     si,[WORD pValidModes]   ; DS:SI -> source index table
        mov     ax,seg ModeTable
        mov     es,ax
        mov     di,offset ModeTable ; ES:DI -> destination index table
        mov     cx,[tableSize]
        cld
    rep movsb                           ; Copy the data

        pop     di
        pop     si
        pop     ds
        pop     bp
        ret

procend     _copyValidModes

;----------------------------------------------------------------------------
; void install(int superVGA,int chipID,int memory,int dac,int pageFlip,
;   int VBECompliant)
;----------------------------------------------------------------------------
;
; This routine installs the VBE driver for the specified SuperVGA
; configuration and returns to the main program.
;
; Registers:    all
;
;----------------------------------------------------------------------------
procstart   _install

        ARG     superVGA:WORD, chipID:WORD, memory:WORD, dac:WORD,      \
                _pageFlip:WORD, _VBECompliant:WORD

        push    bp
        mov     bp,sp
        push    ds

; Store the values for the SuperVGA configuration.

        mov     ax,[superVGA]
        mov     [CntSuperVGA],ax
        mov     ax,[chipID]
        mov     [CntChipID],ax
        mov     ax,[memory]
        mov     [CntMemory],ax
        mov     ax,[dac]
        mov     [CntDac],ax
        mov     ax,[_VBECompliant]
        mov     [VBECompliant],ax

; Store the memory size in the VBE mode information block

        mov     ax,[memory]
        shr     ax,6                ; AX := memory in 64k lots
        mov     [VBEMemSize],al     ; Save the value for later

; Setup the SuperVGA tables and initialise the SuperVGA. Determine if
; dual banking is supported, modifying the video mode information table if
; not.

        call    SetupSuperVGA       ; Setup SuperVGA tables

; Copy the bank switching routines into the appropriate buffers within
; the resident code and re-vector to point to them. We also check to make
; sure they fit in the buffers, failing to install if they don't.

        push    cs
        pop     ds
        push    cs
        pop     es

        mov     si,[WriteBank16]            ; DS:SI -> write bank 16 routine
        mov     di,offset WriteBank16Buf    ; ES:DI -> write bank 16 buffer
        mov     cx,[WriteBank16Len]
        cmp     cx,WRITE_BUF_SIZE
        jg      @@FailInstall               ; Ensure routine will fit
    rep movsb                               ; Copy the routine

        mov     si,[ReadBank16]             ; DS:SI -> read bank 16 routine
        mov     di,offset ReadBank16Buf     ; ES:DI -> read bank 16 buffer
        mov     cx,[ReadBank16Len]
        cmp     cx,READ_BUF_SIZE
        jg      @@FailInstall               ; Ensure routine will fit
    rep movsb                               ; Copy the routine

        mov     si,[WriteBank256]           ; DS:SI -> write bank 256 routine
        mov     di,offset WriteBank256Buf   ; ES:DI -> write bank 256 buffer
        mov     cx,[WriteBank256Len]
        cmp     cx,WRITE_BUF_SIZE
        jg      @@FailInstall               ; Ensure routine will fit
    rep movsb                               ; Copy the routine

        mov     si,[ReadBank256]            ; DS:SI -> read bank 256 routine
        mov     di,offset ReadBank256Buf    ; ES:DI -> read bank 256 buffer
        mov     cx,[ReadBank256Len]
        cmp     cx,READ_BUF_SIZE
        jg      @@FailInstall               ; Ensure routine will fit
    rep movsb                               ; Copy the routine

		mov     si,[NewPage16]              ; DS:SI -> new page routine
		mov     di,offset NewPage16Buf      ; ES:DI -> new page buffer
		mov     cx,[NewPage16Len]
		cmp     cx,PAGE_BUF_SIZE
		jg      @@FailInstall               ; Ensure routine will fit
	rep movsb                               ; Copy the routine

		mov     si,[NewPage256]             ; DS:SI -> new page routine
		mov     di,offset NewPage256Buf     ; ES:DI -> new page buffer
		mov     cx,[NewPage256Len]
		cmp     cx,PAGE_BUF_SIZE
		jg      @@FailInstall               ; Ensure routine will fit
	rep movsb                               ; Copy the routine

		mov     si,[SetupSVGA]              ; DS:SI -> new setup routine
        mov     di,offset SetupBuf          ; ES:DI -> new setup buffer
        mov     cx,[SetupSVGALen]
        cmp     cx,SETUP_BUF_SIZE
        jg      @@FailInstall               ; Ensure routine will fit
    rep movsb                               ; Copy the routine

        mov     si,[ExitSVGA]               ; DS:SI -> new exit routine
        mov     di,offset ExitBuf           ; ES:DI -> new exit buffer
        mov     cx,[ExitSVGALen]
        cmp     cx,EXIT_BUF_SIZE
        jg      @@FailInstall               ; Ensure routine will fit
    rep movsb                               ; Copy the routine

        mov     si,[Set8BitDac]             ; DS:SI -> new 8 bit DAC routine
        mov     di,offset ExitBuf           ; ES:DI -> new 8 bit DAC buffer
        mov     cx,[Set8BitDacLen]
        cmp     cx,DAC_BUF_SIZE
        jg      @@FailInstall               ; Ensure routine will fit
    rep movsb                               ; Copy the routine

        mov     si,[Set6BitDac]             ; DS:SI -> new 6 bit DAC routine
        mov     di,offset ExitBuf           ; ES:DI -> new 6 bit DAC buffer
        mov     cx,[Set6BitDacLen]
        cmp     cx,DAC_BUF_SIZE
        jg      @@FailInstall               ; Ensure routine will fit
    rep movsb                               ; Copy the routine

ifndef  TESTING

; Revector to use the re-located versions of the routines. This will only
; be done when testing is not on, so you can still debug the routines.

        mov     [WriteBank16],offset WriteBank16Buf
        mov     [ReadBank16],offset ReadBank16Buf
        mov     [WriteBank256],offset WriteBank256Buf
        mov     [ReadBank256],offset ReadBank256Buf

		cmp     [NewPage16],NOT_VALID
        je      @@1
        mov     [NewPage16],offset NewPage16Buf
        mov     [NewPage256],offset NewPage256Buf

@@1:    cmp     [SetupSVGA],NOT_VALID
        je      @@2
        mov     [SetupSVGA],offset SetupBuf

@@2:    cmp     [ExitSVGA],NOT_VALID
        je      @@3
        mov     [ExitSVGA],offset ExitBuf

@@3:    cmp     [Set8BitDac],NOT_VALID
        je      @@4
        mov     [Set8BitDac],offset Set8BitDacBuf
        mov     [Set6BitDac],offset Set6BitDacBuf

endif

@@4:    cmp     [TwoBanks],true
        je      @@DualBank

        mov     [WinBAttr],0        ; Remove support for second video bank

@@DualBank:
        call    HookVectors         ; Hook interrupt vectors
        mov     ax,true             ; Installed correctly
        pop     ds
        pop     bp
        ret

@@FailInstall:
        mov     ax,false            ; Could not install!!!
        pop     ds                  ; Restore DS
        pop     bp
        ret

procend     _install

;----------------------------------------------------------------------------
; void goTSR(void)
;----------------------------------------------------------------------------
;
; This routine exists to the operating system leaving the required
; code resident in memory.
;
; If we are in testing mode, the routine does not go TSR but simply
; calls the 'C' testing routine, unhooks the interrupts and exits.
;
; Registers:    all
;
;----------------------------------------------------------------------------
procstart   _goTSR

ifndef  TESTING

; Now setup to go TSR with resident code.

        mov     ax,[__psp]
        mov     es,ax
        xor     ax,ax
        xchg    ax,[es:2Ch]         ; AX := seg of environment block
        mov     es,ax               ; ES := seg of environment block
        mov     ah,49h
        int     21h                 ; Free the environment block

        mov     ah,31h
        mov     al,[BYTE CntSuperVGA] ; Return SuperVGA id as result code
        mov     dx,offset EndResident
        add     dx,PSPSize + 15     ; PSP is always resident!!
        shr     dx,4
        int     21h                 ; Go TSR!

else

; Call the C testing routine, unhook vectors and quit.

        call    _testISR            ; Call the test routine
        call    UnhookVectors       ; Unhook vectors
        ret

endif

procend     _goTSR

;----------------------------------------------------------------------------
; HookVectors   - Hook into the interrupt vectors that we require.
;----------------------------------------------------------------------------
;
; Saves the value of the old interrupt vectors that we require, and
; install's hooks to our code for the vectors.
;
; Registers:    all
;
;----------------------------------------------------------------------------
PROC    HookVectors near

; Save old interrupt vectors

        GetVector   10h
        mov     [cs:OldInt10Off],bx
        mov     [cs:OldInt10Seg],es

; Insert our interrupt vectors into vector table

        SetVector   10h, Int10ISR
        ret

ENDP    HookVectors

;----------------------------------------------------------------------------
; UnhookVectors - Unhook from all interrupt vectors that we own.
;----------------------------------------------------------------------------
;
; Restores the interrupt vector table to what it was before we installed
; ourselves.
;
; Registers:    all
;
;----------------------------------------------------------------------------
PROC    UnhookVectors

        push    ds
        mov     dx,[OldInt10Seg]
        mov     ds,dx
        mov     dx,[OldInt10Off]
        mov     ah,25h
        mov     al,10h
        int     21h
        pop     ds
        ret

ENDP    UnhookVectors

endcodeseg  main

        END
