;****************************************************************************
;*
;*						  The Universal VESA TSR
;*
;*                  Copyright (C) 1993 Kendall Bennett.
;*							All rights reserved.
;*
;* Filename:	$RCSfile: univesa.asm $
;* Version:		$Revision: 1.2 $
;*
;* Language:	80386 Assembler
;* Environment:	IBM PC (MS DOS)
;*
;* Description:	This source file contains the code for a Universal VESA
;*				TSR's, to implement the VESA video BIOS extensions for
;*				supported SuperVGA's.
;*
;*				Must be assembled in the large memory model.
;*
;* $Id: univesa.asm 1.2 1993/03/07 04:05:36 kjb Exp $
;*
;* Revision History:
;* -----------------
;*
;* $Log: univesa.asm $
;* Revision 1.2  1993/03/07  04:05:36  kjb
;* Bug fixes.
;*
;* Revision 1.1  1993/03/03  10:27:33  kjb
;* Initial revision
;*
;****************************************************************************

		IDEAL
        DOSSEG
		JUMPS
		P386						; Use 386 instructions

INCLUDE "MODEL.MAC"					; Memory model macros

header  uvesa                       ; Link with uvesa.c

;----------------------------------------------------------------------------
; 		Internal Equates and Macros
;----------------------------------------------------------------------------

INCLUDE	"MGRAPH.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
VESA_FUNCTIONS	EQU	04Fh			; VESA Control function value (AH)
MAX_FUNCTION	EQU	7				; Highest VESA function number supported
VESA_VERSION	EQU	0102h			; Version 1.2

; 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

begcodeseg	uvesa

		EXTRN   _testISR:FAR

endcodeseg	uvesa

begcodeseg	modes

		EXTRN	_MGL_availableModes:FAR

endcodeseg	modes

begcodeseg	uvesa

;----------------------------------------------------------------------------
; 		Resident Data
;----------------------------------------------------------------------------

; Signature used to determine if the TSR is already installed.

Signature:			db	'Universal VESA TSR',0
SigLen				=	$-Signature

LABEL		OldInt10	DWORD
OldInt10off			dw	?					; Place to save int 10h vector
OldInt10seg			dw	?

; Globals used by resident generic SuperVGA routines

CntDriver			dw	0					; Graphics driver number
CntMode				dw	0					; Graphics mode number
CntDAC				dw	0					; Video DAC ID
CntChipID			dw	0					; Graphics driver chip ID
CntColors			dw	0					; Current color mode
CntVESAMode			dw	0					; Current VESA mode number
CntMemory			dw	0					; Amount of memory on chip
CntStartX			dw	0					; Current display start X
CntStartY			dw	0					; Current display start Y

; Dispatch table for supported function calls

DispatchTable		dw	offset SuperVGAInfo
					dw	offset SuperVGAModeInfo
					dw	offset SuperVGASetMode
					dw	offset SuperVGAGetMode
					dw	offset SuperVGAVideoState
					dw	offset SuperVGABankSwitch
					dw	offset SuperVGAScanlineLength
					dw	offset SuperVGADisplayStart

Status				dw	0					; Return status code

; VGA Information block returned to the user program

VGAInfo				db	'VESA'				; Signature value
					dw	VESA_VERSION		; VESA 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
VGAInfoSize			=	$-VGAInfo

; List of all possible supported VESA video modes. If any of the modes
; are not supported by the installed SuperVGA, they are zeroed from the
; table.

ModeTable			dw	100h				; grSVGA_640x400x256
					dw	101h				; grSVGA_640x480x256
					dw	102h				; grSVGA_800x600x16
					dw	103h				; grSVGA_800x600x256
					dw	104h				; grSVGA_1024x768x16
					dw	105h				; grSVGA_1024x768x256
					dw	106h				; grSVGA_1280x1024x16
					dw	107h				; grSVGA_1280x1024x256
					dw	10Dh				; grSVGA_320x200x32k
					dw	10Fh				; grSVGA_320x200x16m
					dw	110h				; grSVGA_640x480x32k
					dw	112h				; grSVGA_640x480x16m
					dw	113h				; grSVGA_800x600x32k
					dw	115h				; grSVGA_800x600x16m
					dw	116h				; grSVGA_1024x768x32k
					dw	118h				; grSVGA_1024x768x16m
					dw	119h				; grSVGA_1280x1024x32k
					dw	11Bh				; grSVGA_1280x1024x16m
					dw	0FFFFh				; Terminate the list

; Define the memory model type numbers

memPL16				EQU	3					; Planar
memPK256			EQU 4					; Packed pixel
memPKD				EQU	6					; Direct

; Video mode information for the currently active video mode. 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
					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	8					; Character width
CHeight				db	8					; 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
					db	0
Pages				db	0					; Number of image pages
ModeInfoSize		=	$-ModeInfo

; Lookup table for filling in the optional mode information in the above
; structure.

mode800x600x16		dw	800,600
					db	8,8,4,4,1,memPL16
mode1024x768x16		dw	1024,768
					db	8,8,4,4,1,memPL16
mode1280x1024x16	dw	1280,1024
					db	8,8,4,4,1,memPL16
PartialInfoSize		=	$-mode1280x1024x16

mode640x350x256		dw	640,350
					db	8,8,1,8,1,memPK256
mode640x400x256		dw	640,400
					db	8,8,1,8,1,memPK256
mode640x480x256		dw	640,480
					db	8,8,1,8,1,memPK256
mode800x600x256		dw	800,600
					db	8,8,1,8,1,memPK256
mode1024x768x256	dw	1024,768
					db	8,8,1,8,1,memPK256
mode1280x1024x256	dw	1280,1024
					db	8,8,1,8,1,memPK256

mode320x200x32k		dw	320,200
					db	8,8,1,15,1,memPKD
mode640x350x32k		dw	640,350
					db	8,8,1,15,1,memPKD
mode640x400x32k		dw	640,400
					db	8,8,1,15,1,memPKD
mode640x480x32k		dw	640,480
					db	8,8,1,15,1,memPKD
mode800x600x32k		dw	800,600
					db	8,8,1,15,1,memPKD
mode1024x768x32k	dw	1024,768
					db	8,8,1,15,1,memPKD
mode1280x1024x32k	dw	1280,1024
					db	8,8,1,15,1,memPKD

mode320x200x16m		dw	320,200
					db	8,8,1,24,1,memPKD
mode640x350x16m		dw	640,350
					db	8,8,1,24,1,memPKD
mode640x400x16m		dw	640,400
					db	8,8,1,24,1,memPKD
mode640x480x16m		dw	640,480
					db	8,8,1,24,1,memPKD
mode800x600x16m		dw	800,600
					db	8,8,1,24,1,memPKD
mode1024x768x16m	dw	1024,768
					db	8,8,1,24,1,memPKD
mode1280x1024x16m	dw	1280,1024
					db	8,8,1,24,1,memPKD

;----------------------------------------------------------------------------
; 		Resident Code
;----------------------------------------------------------------------------

		ASSUME  cs:uvesa_TEXT, ds:NOTHING, es:NOTHING

INCLUDE "SV_PORTS.ASM"
INCLUDE "SV_BANKS.ASM"
INCLUDE "SV_MODES.ASM"
INCLUDE "SV_PAGE.ASM"
INCLUDE "SV_MAXPG.ASM"

;----------------------------------------------------------------------------
; 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 VESA functions
; calls, and handle them appropriately, or else we simply pass the call
; to the old Video BIOS handler.
;
;----------------------------------------------------------------------------
PROC	Int10ISR	far

		cmp		ah,VESA_FUNCTIONS	; Is this a VESA function?
		je		@@VesaFunction		; 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,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
		call	SuperVGASetMode		; Set the video mode
		iret

@@ToOldBIOS:
		jmp		[OldInt10]			; Jump to old VIDEO BIOS

; This is a VESA function, so dispatch to the appropriate routine depending
; on the subfunction number.

@@VesaFunction:
		cmp		al,MAX_FUNCTION
		ja		@@NotSupported		; VESA Function is not supported

		cmp		al,5				; Bank Switching call?
		jne		@@NotBankSwitch		; No, handle normally

; The bank switch routine can be called as a far function, so we can't
; handle this routine with a simple lookup table.

		call	far SuperVGABankSwitch
		iret

@@NotBankSwitch:
		xor		ah,ah
		shl		al,1				; AX := index into table
		xchg	ax,bx
		mov		bx,[DispatchTable + bx]
		xchg	ax,bx
		mov		[Status],FAILURE
		push	ds					; Save value of DS
		call	ax					; Call the appropriate function
        pop		ds					; Restor value of DS
		mov		ax,[Status]			; Return status code
		iret

@@NotSupported:
		xor		al,al				; Function is not supported
		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		- VESA status code
;
;----------------------------------------------------------------------------
PROC	SuperVGAInfo	near

		pusha						; Save register values
		push	ds					; Save DS

		cld
		mov		ax,cs
		mov		ds,ax
		mov		si,offset VGAInfo	; DS:SI -> VGAInfo Table
		mov		cx,VGAInfoSize
	rep	movsb						; Copy the data across
		mov		[Status],SUCCESS	; Return status code

		pop		ds					; Restore DS
		popa						; Restore register values
		ret

ENDP	SuperVGAInfo

;----------------------------------------------------------------------------
; SuperVGAModeInfo	- Get SuperVGA Video Mode Information
;----------------------------------------------------------------------------
;
; Function to return information about a specific VESA SuperVGA video mode.
;
; Entry:		CX		- SuperVGA video mode
;				ES:DI	-> 256 byte buffer for mode information
;
; Exit:			AX		- VESA status code
;
;----------------------------------------------------------------------------
PROC	SuperVGAModeInfo	near

		pusha						; Save register values

		call	VESAToMGLMode		; AX := MGL mode number
		cmp		ax,-1
		je		@@Invalid			; Mode is invalid to us!

		push	[BytesPerLine]		; Save current bytes per line value
		push	ax					; Save MGL mode number

		xor		ebx,ebx
		mov		bx,[CntMemory]
		shl 	ebx,10				; EBX := memory in bytes
		call	numPages			; Compute number of video pages
		mov		[Pages],al			; Store number of video pages
		mov		[BytesPerLine],cx	; Stote bytes per line value

; Copy the information specific to the video mode

		pop		ax					; AX := MGL mode number
		sub		ax,grSVGA_800x600x16
		mov		cx,PartialInfoSize
		mul		cl
		mov		si,offset mode800x600x16
		add		si,ax
		push	es					; Save ES:DI
		push	di
		mov		ax,cs
		mov		ds,ax				; DS:SI -> correct mode info
		mov		es,ax
		mov		di,offset XRes		; ES:DI -> mode specific info
		cld
	rep	movsb						; Copy the data across

		pop		di					; Restore ES:DI
		pop		es

; Copy the mode information across

		mov		si,offset ModeInfo	; DS:SI -> VGAInfo Table
		mov		cx,ModeInfoSize
	rep	movsb						; Copy the data across

		pop		[BytesPerLine]		; Restore bytes per line value
		mov		[Status],SUCCESS
		popa
		ret

; The video mode is invalid, but the underlying video BIOS may be VESA
; compatible, and the mode number may mean something to it, so we pass it
; on to it.

@@Invalid:
		popa
		mov		ax,4F01h
		pushf
		call	[OldInt10]			; Call the old BIOS
		mov		[Status],ax			; Save the status of the call
		ret

ENDP	SuperVGAModeInfo

;----------------------------------------------------------------------------
; SuperVGASetMode	- Set SuperVGA Video Mode
;----------------------------------------------------------------------------
;
; Function to set a specific VESA SuperVGA video mode. Only graphics modes
; are supported by this TSR.
;
; Entry:		BX	- VESA video mode number (bit 15 means don't clear mem).
;
; Exit:			AX	- VESA status code.
;
;----------------------------------------------------------------------------
PROC	SuperVGASetMode		near

		pusha						; Save register values
		push	es

		mov		cx,bx
		and		cl,07Fh
		cmp		cx,13h
		ja		@@VesaMode

; Attempting to set a standard VGA video mode, so convert the mode number
; into an MGL video mode number and skip the stuff below to set a
; VESA video mode.

		push	cx
		push	bx

		push	cs
		pop		es
		mov		di,offset VGAModes	; ES:DI -> list of VGA video modes
		mov		ax,cx
		mov		cx,12
		repne	scasw				; Search for entry
		inc		cx
		shr		cx,1
		sub		cx,6
		neg		cx					; CX := MGL mode number
		mov		[CntMode],cx

		pop		ax
		pushf
		call	[OldInt10]			; Set the video mode
		pop		cx
		jmp		@@StandardVGAMode

@@VesaMode:
		push	bx					; Save VESA mode number for later
		mov		cx,bx
		and		ch,07Fh				; Clear memory clear bit
		push	cx
		call	VESAToMGLMode		; AX := internal MGL video mode
		cmp		ax,-1
		jne		@@1

		pop		bx
		pop		bx
		jmp		@@InvalidMode		; Video mode is invalid to us!

@@1:	mov		[CntMode],ax		; Save internal MGL video mode
		mov		bx,ax				; BX := current video mode
		mov		ax,[CntDriver]		; AX := current video driver
		call	loadSVGAMode		; AX,BX := correct value for Int 10h
		pop		cx					; CX := VESA 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:
		pushf
		call	[OldInt10]			; Set the video mode

		pop		cx					; CX := VESA mode number

		mov		ax,40h
		mov		es,ax
		cmp		[BYTE es:49h],3		; Mode is still text mode, did not set
		jbe		@@ModeSetFailed

@@StandardVGAMode:
		mov		[CntVESAMode],cx	; Save VESA mode now set
		call	SetupBanks
		call	[InitSVGA]			; Initialise bank switching on SuperVGA's
		call	SetupPaging

		mov		ax,[CntMode]		; AX := current video mode number
		mov		ebx,4096 shl 10		; Compute with maximum video memory
		call	numPages			; Compute bytes per line value
		mov		[BytesPerLine],cx	; Save BytesPerLine value

; Determine the color resolution of the video mode and save for later

		mov		ax,[CntMode]		; AX := current MGL video mode
		cmp		ax,grVGA_320x200x256
		je		@@Colors256
		cmp		ax,grSVGA_1280x1024x16
		jbe		@@Colors16
		cmp		ax,grSVGA_1280x1024x256
		jbe		@@Colors256
		cmp		ax,grSVGA_1280x1024x32k
		jbe		@@Colors32k

		mov		[CntColors],gr16mColor
		jmp		@@Done

@@Colors16:
		mov		[CntColors],gr16Color
		jmp		@@Done
@@Colors256:
		mov		[CntColors],gr256Color
		jmp		@@Done
@@Colors32k:
		mov		[CntColors],gr32kColor

@@Done:
		mov		[CntStartX],0
		mov		[CntStartY],0
		mov		[Status],SUCCESS

@@ModeSetFailed:
		pop		es
		popa
		ret

; The mode number was invalid to us, but may make sense to an underlying
; VESA compatible BIOS.

@@InvalidMode:
		pop		es
		popa
		mov		ax,4F02h
		pushf
		call	[OldInt10]			; Call the old BIOS
		mov		[Status],ax			; Save the status value
		mov		[CntVESAMode],-1	; Flag that mode is unknown to us!
		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 VESA video mode number for the mode is returned.
;
; Exit:			AX	- VESA status code
;				BX	- Current video mode
;
;----------------------------------------------------------------------------
PROC	SuperVGAGetMode		near

		push	es
		mov		ax,40h
		mov		es,ax
		xor		bx,bx
		mov		bl,[BYTE es:49h]
		cmp		bl,13h
		mov		[Status],SUCCESS
		jbe		@@VGAMode

		mov		bx,[CntVESAMode]	; VESA Video mode is active, so return it
		cmp		bx,-1
		jne		@@VGAMode

; The mode was not know to us, so it may be an underlying VESA video mode.

		mov		ax,4F03h
		pushf
		call	[OldInt10]
		mov		[Status],ax

@@VGAMode:
		pop		es
		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:			AH		- VESA Status code
;				BX		- Number of 64 byte blocks needed to save state
;
;----------------------------------------------------------------------------
PROC	SuperVGAVideoState	near

; First try calling the underlying video BIOS which may be VESA compatible.

		pusha
		pushf
		call	[OldInt10]			; Call the old BIOS
		mov		[Status],ax
		popa
		cmp		[Status],SUCCESS
		je		@@Done				; Succeeded, we are all done!

; Otherwise try to do as much as possible with as little knowledge as
; possible.

		push	dx
		push	cx
		mov		ah,1Ch				; Save/Restore video state BIOS call
		mov		al,dl				; AL := subfunction number
		pushf
		call	[OldInt10]			; Call the old BIOS
		pop		cx
		pop		dx
		cmp		al,1Ch
		jne		@@Failure

		test	cx,010b
		jz		@@Done
		cmp		dl,2
		jne		@@Done

; The Video BIOS state was saved, so call the BIOS to set the current
; video mode again, and then restore the video state once more to complete
; things.

		push	cx
		push	dx
		push	es
		mov		ax,40h
		mov		es,ax
		xor		ah,ah
		mov		al,[BYTE es:49h]
		pushf
		call	[OldInt10]			; Set the old video mode
		pop		es
		pop		dx
		pop		cx

		mov		ah,1Ch
		mov		al,2
		pushf
		call	[OldInt10]			; Restore video state again

@@Done:
		mov		[Status],SUCCESS

@@Failure:
		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	- VESA status code.
;				DX	- Window address in video memory (when requested)
;
;----------------------------------------------------------------------------
PROC	SuperVGABankSwitch	far

		pusha
		or		bh,bh
		jnz		@@Done				; Simply return the current window

		mov		ax,dx				; AX := bank number
		or		bl,bl
		jnz		@@WindowB
		call	[NewBank]			; Set new read/write bank
		jmp		@@Done

@@WindowB:
		call	[ReadBank]			; Set new read bank

@@Done:
		popa
		mov		dx,[CurBank]		; Return the current bank number
		mov		[Status],SUCCESS
		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	- VESA status code
;				BX	- Bytes per scanline
;				CX	- Pixels per scanline
;               DX	- Maximum number of scanlines
;
;----------------------------------------------------------------------------
PROC	SuperVGAScanlineLength	near

		pusha
		mov		ax,[CntColors]
		mov		[Status],SUCCESS
		or		bh,bh
		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		@@SetOffsetReg
		cmp		ax,gr32kColor
		je		@@Colors32k

		mov		ax,cx
		shl		cx,1
		add		cx,ax				; CX := bytes per scanline
		and		cx,0FFF8h			; Mask off bottom three bits
		mov		[BytesPerLine],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		[BytesPerLine],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		[BytesPerLine],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		[BytesPerLine],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

@@GetScanlineLength:
		popa
		mov		bx,[BytesPerLine]	; BX := bytes per scanline
		cmp		ax,gr16Color
		je		@@GetColors16
		cmp		ax,gr256Color
		je		@@GetColors256
		cmp		ax,gr32kColor
		je		@@GetColors32k

		mov		ax,cx
		shr		cx,1
		sub		cx,ax				; CX := pixels per scanline
		ret

@@GetColors16:
		shl		cx,3				; CX := pixels per scanline
		ret

@@GetColors256:
		ret

@@GetColors32k:
		shr		cx,1				; CX := pixels per scanline
		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

		cmp		[NewPage],offset NONE_page
		je		@@Failure

		pusha
		or		bh,bh
		jnz		@@GetDisplayStart

; Set the starting display address.

		xor		eax,eax
		and		edx,0FFFFh			; EDX := first scanline
		and		ecx,0FFFFh			; ECX := leftmost pixel
		mov		ax,[BytesPerLine]
		mul		edx					; EAX := y * bytesPerLine

		mov		bx,[CntColors]
		cmp		bx,gr16Color
		je		@@Colors16
		cmp		bx,gr256Color
		je		@@Colors256
		cmp		bx,gr32kColor
		je		@@Colors32k

; Video mode is a 24 bit video mode

		add		eax,ecx				; EAX := x + y * bytesPerLine
		shl		ecx,1
		add		eax,ecx				; EAX := x * 3 + y * bytesPerLine
		shr		eax,2				; Adjust to lie on plane boundary
		jmp		@@SetDisplayStart

; Video mode is a 16 color video mode

@@Colors16:
		shr		ecx,3
		add		eax,ecx				; EAX := x/8 + y * bytesPerLine
		jmp		@@SetDisplayStart

; Video mode is a 256 color video mode

@@Colors256:
		add		eax,ecx				; EAX := x + y * bytesPerLine
		shr		eax,2				; Adjust to lie on plane boundary
		jmp		@@SetDisplayStart

; Video mode is a 32k color video mode

@@Colors32k:
		shl		ecx,1
		add		eax,ecx				; EAX := x + y * bytesPerLine
		shr		eax,2				; 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
		shr		eax,16
		mov		si,ax				; SI := Bits 16+ for SuperVGA's

		call	[NewPage]			; Program the start address

		mov		[CntStartX],cx		; Save the display starting coordinates
		mov		[CntStartY],dx
		mov		[Status],SUCCESS
		popa
		ret

@@GetDisplayStart:
		popa
		mov		cx,[CntStartX]		; Return saved starting coordinates
		mov		dx,[CntStartY]
		mov		[Status],SUCCESS
@@Failure:
		ret

ENDP	SuperVGADisplayStart

;----------------------------------------------------------------------------
; VESAToMGLMode	- Converts a VESA mode number to an MGL mode number
;----------------------------------------------------------------------------
;
; Internal routine to convert a VESA mode number to an MGL mode number.
; MGL Mode numbers are the ones that we use internally, since the code is
; based on that from the MegaGraph graphics library.
;
; Entry:		CX	- VESA mode number
;
; Exit:			AX	- MGL mode number (-1 if not found)
;
; Registers:	AX,CX
;
;----------------------------------------------------------------------------
PROC	VESAToMGLMode	near

		push	bx
		push	ds
		push	si
		push	di

; First check to ensure that the VESA mode number is valid for the currently
; installed SuperVGA adapter.

		push	cs
		pop		ds
		mov		si,offset ModeTable	; DS:SI -> Valid VESA mode table

@@VESALoop:
		lodsw						; Load next valid VESA mode number
		cmp		ax,-1
		je		@@InvalidMode		; Hit the end of the table
		cmp		ax,cx				; Is this the one?
		je		@@ValidMode			; Yes, so mode is valid
		jmp		@@VesaLoop			; No, so keep looking...

; Convert the VESA mode number to the MGL mode number by searching the
; conversion table.

@@ValidMode:
		mov		si,offset VesaModes	; DS:SI -> VESA/MGL mode table

@@ModeLoop:
		lodsw						; AX := VESA mode number
		or		ax,ax
		jz		@@InvalidMode		; Exit if mode is invalid
		mov		bx,ax				; BX := VESA mode number
		lodsw						; AX := MGL mode number
		cmp		bx,cx				; Found the video mode?
		je		@@Exit				; Yes, found the mode...
		jmp		@@ModeLoop

@@InvalidMode:
		mov		ax,-1				; Video mode not found

@@Exit:
		pop		di
		pop		si
		pop		ds
		pop		bx
		ret

ENDP	VESAToMGLMode

EndResident:						; End of resident code

;----------------------------------------------------------------------------
;		Transient Code.
;----------------------------------------------------------------------------

		ASSUME  cs:uvesa_TEXT, ds:DGROUP, es:NOTHING

;----------------------------------------------------------------------------
; bool isLoaded(void)
;----------------------------------------------------------------------------
; Returns true if the Universal VESA TSR is already loaded in memory.
;----------------------------------------------------------------------------
procstart	_isLoaded

		enter	0,0
		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
		leave
		ret

procend		_isLoaded

;----------------------------------------------------------------------------
; void goTSR(int driver,int chipID,int memory,int dac)
;----------------------------------------------------------------------------
;
; This routine installs the VESA driver for the specified SuperVGA
; configuration, and exits 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

		ARG		driver:WORD, chipID:WORD, memory:WORD, dac:WORD

		enter	0,0

; Store the values for the SuperVGA configuration.

		mov		ax,[driver]
		mov		[CntDriver],ax
		mov		ax,[chipID]
		mov		[CntChipID],ax
		mov		ax,[memory]
		mov		[CntMemory],ax
		mov		ax,[dac]
		mov		[CntDac],ax

; Now determine the list of available video modes for this adapter.

		push    [CntMemory]
		push	[CntDriver]
		call	_MGL_availableModes	; Determine available video modes
		add     sp,4

		push	ds
		mov		ds,dx
		mov		si,ax				; DS:SI -> MGL video mode table
		push	cs
		pop		es
		mov		di,offset ModeTable	; ES:DI -> Video Mode Table
		mov		bx,offset VesaModes ; CS:BX -> All VESA Modes

@@ModeLoop:
		mov		cx,[cs:bx]			; CX := VESA Mode Number
		jcxz	@@DoneModes			; Quit when table exhausted
		mov		dx,[cs:bx+2]		; DX := MGL Mode Number
		add		bx,4				; CS:BX -> next mode in table

		push	si					; Save start of MGL mode table

@@FindMode:
		lodsw						; AX := MGL video mode number
		cmp		ax,-1
		je		@@NotFound			; Mode was not found
		cmp		ax,dx
		je		@@Found				; Mode number was found
		jmp		@@FindMode			; Continue till mode is found

@@Found:
		mov		ax,cx
		stosw						; Store the appropriate mode number

@@NotFound:
		pop		si					; DS:SI -> MGL video mode table
		jmp		@@ModeLoop			; Continue till table exhausted

; Setup the bank switching and page flipping code, and determine if dual
; banking is supported, modifying the video mode information table if
; not.

@@DoneModes:
		mov		ax,0FFFFh
		stosw						; Terminate the mode list
		pop		ds					; Restore DS
		call	SetupBanks			; Setup bank switching
		call	[InitSVGA]			; Initialise SuperVGA code
		call	SetupPaging			; Setup page flipping
		cmp		[TwoBanks],true
		je		@@DualBank

		mov		[WinBAttr],0		; Remove support for second video bank

@@DualBank:
		call	HookVectors			; Hook interrupt vectors

ifndef  TESTING

; Now setup to go TSR with resident code.

		mov		ah,31h
		xor		al,al
		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
		mov		ah,04Ch
		mov		al,1				; Error code of 1
		int		21h					; Exit to DOS

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	uvesa

		END
