
;SVESA.INC - Copyright 1991-1995 Knight Software
;    History:
;       17 May 1991 - first release
;       19 Aug 1992 - fixed bug with seperate read/write windows. 
;                     Fixed push/pop bug in VesaBank select
;       22 Nov 1992 - adapted for protected mode operation
;	25 Oct 1994 - added pageing code
;       25 Nov 1994 - added virtualBGI support code
;	12 Dec 1994 - no longer allows selection of bad mode
;	               (graph unit does not allow close down if bad)
;       02 Feb 1995 - added test for defective vesa bios func 6 and removed
;                      hardware call for scanline length the vesa call (6)
;                      has to work or no virtual scan length allowed
;       21 May 1995 - added test for vesa info failure for mode 13h
;                      now uses default information on failure 
;
;----------------------------------------------------------
;This file provides VESA support


;----------------------------------------------------------
;Vesa mode information block returned by get vesa info call.
;This is temporary information is is only available within
;the DetectCard call (located in TempWork).

ModeInfoBlock struc
  ModeAttributes     DW 0  ;mandatory information
  WinAAttributes     DB 0  ;this is always returned
  WinBAttributes     DB 0
  WinGranularity     DW 0
  WinSize            DW 0
  WinASegment        DW 0
  WinBSegment        DW 0
  WinFuncPtr         DD 0
  BytesPerScanLine   DW 0
  ;------------------------
  XResolution        DW 0  ;this info only available if
  YResolution        DW 0  ;bit 1 of ModeAttributes is set
  XCharSize          DB 0
  YCharSize          DB 0
  NumberOfPlanes     DB 0
  BitsPerPixel       DB 0
  NumberOfBanks      DB 0
  MemoryModel        DB 0
  BankSize           DB 0 
  NumberOfImagePages DB 0
ModeInfoBlock ends


;----------------------------------------------------------
;DetectCard detects whether the VESA support is out there.
;If VESA support is found, then the highest supported 
;256 color mode is determined. If no VESA support is found, 
;we default to standard VGA mode. Note: status table is not
;setup until after this call is complete (see MiscInit).
;Assumed: DS=data segment 
;Enter: Nothing
;Return: AL=status error result (see error list in SDATA.INC)
;External Updates: 
;	VideoSegment, NumberModes, VidMemSize, DisplayType 
;       ModeErrorFlag, ScanLineBytes, DisplayPages
;       BankSelectProc, InitDisplayProc, CardNamePtr
;Internal Updates: 
;	WinGran, VidMode


DetectCard PROC NEAR
	PUSH	ES
	PUSH	DI
	PUSH	SI
	PUSH	DX
	PUSH	CX
	PUSH	BX

	MOV	BX,DS:[SEGA000]	   	;init video segment/selector
	MOV	DS:[VideoSegment],BX
	MOV	BX,0FFFFH		;Init bank select reg
	MOV	DS:[PixelSegment],BX

	MOV	BX,0
	MOV	DS:[VidMode],BX       	;init VESA mode to zero
	MOV	DS:[NumberModes],BX   	;and max mode
	MOV	DS:[VidMemSize],BX	;and memory available
	MOV	AL,grOK
	MOV	DS:[ModeErrorFlag],AL 	;preclear error flag
	MOV	AX,4F00H		;get VESA display info
	MOV	DI,OFFSET TempWork
	CALL	VESAInt
	JC	@NoVESA			;dpmi call failed - sigh
	CMP	WORD PTR ES:[DI],"EV"
	JNZ	@NoVESA			;error out if not VESA
	CMP	WORD PTR ES:[DI+2],"AS"
	JZ	@IsVESA
@NoVESA:
	JMP	@NotVESA

@IsVESA:
	MOV	SI,ES:[DI+0EH]	;CX:SI = ptr to table of avail modes
	MOV	CX,ES:[DI+10H]

	CMP DS:[CurOpMode],0  	;if CurOpMode is zero (real mode),
	JZ	@IsVESAchk	;no need to convert it

	PUSH	SI
	MOV	AX,0002H	;is prot mode, so gotta convert
	MOV	BX,CX		;real segment to a selector
	INT	31H
	POP	SI
	JC	@NoVESA		;ack, something went wrong
	MOV	CX,AX		;stick selector value in CX

@IsVESAchk:
	MOV	ES,CX		;ES:SI now points to mode table
	SUB	SI,2		;preadj pointer
@NextMode:
	ADD	SI,2
	MOV	AX,ES:[SI]	;get item from vesa list
	CMP	AX,-1		;last item?
	JZ	@LastMode
	MOV	CX,VESAmodeSize	    ;CX=our table length
	MOV	DI,OFFSET VESAmodes ;DS:DI = modes we use
	MOV	BX,0		    ;BX=test mode number
@ModeLoop:
	CMP	AX,CS:[DI]	    ;Is the item in our list?
	JZ	@GoodMode
	ADD	DI,2		    ;try everything in our list
	INC	BX
	LOOP	@ModeLoop
	JMP	SHORT @NextMode	    ;go try next vesa mode
@GoodMode:
	MOV	BH,0
	CMP	BL,DS:[ModeSelect]
	JNZ	@NotOurMode
	MOV	DS:[VidMode],AX     ;Save VESA mode number
@NotOurMode:
	CMP	BX,DS:[NumberModes] ;is mode higher than last one? 
	JL	@NextMode
	MOV	DS:[NumberModes],BX ;update max mode if higher
	JMP	SHORT @NextMode     ;check on next vesa mode

@LastMode:
	MOV	CX,DS:[VidMode]
	OR	CX,CX		      ;if Zero not valid mode
	JNZ	@IsVesaMode	      ;is VESA mode 
	CMP	BYTE PTR DS:[ModeSelect],0 ;if mode zero
	JZ	@VESA13		      ;is OK not to be VESA mode
	MOV	AL,grInvalidMode      ;set error flag
	MOV	DS:[ModeErrorFlag],AL ;mark as mode error
	JMP	@NotVESA	      ;and go to default mode

@VESA13:
;	JMP	@NotVESA

	MOV	CX,13H		    ;use mode 13h as default
	MOV	DS:[VidMode],CX
	MOV	AX,4F01H	    ;get mode 13 info if available
	MOV	DI,OFFSET TempWork  ;(mode is in CX)
	CALL	VESAInt		    ;buffer of data is 
	JNC	@ModeOK		    ; returned in ES:DI
	JMP	@DefVesa13	    ;nope, just use dummy info

@IsVesaMode:
	MOV	AX,4F01H	    ;get mode info
	MOV	DI,OFFSET TempWork  ;(mode is in CX)
	CALL	VESAInt		    ;buffer of data is 
	JC	@NotVESA	    ; returned in ES:DI

@ModeOK:
	MOV	CX,ES:[DI].ModeInfoBlock.WinGranularity
	OR	CX,CX		    ;Get window granularity
	JZ	@NotVESA	    ;if zero, not a valid value
	MOV	AX,64		    ;convert to 64K multiplier
	XOR	DX,DX		    ;for bank selects
	DIV	CX
	MOV	DS:[WinGran],AX	          ;Save it for later
	OR	AX,AX
	JZ	@NotVESA	          ;bad window granularity
	MOV	BX,ES:[DI].ModeInfoBlock.BytesPerScanLine
	MOV	DS:[ScanLineBytes],BX     ;save real scan line length
	MOV	AL,1			  ;default to one page
	TEST	DS:[DI].ModeInfoBlock.ModeAttributes,02H
	JZ	@SimpleVESA	          ;img page info not available
	MOV	AL,ES:[DI].ModeInfoBlock.NumberOfImagePages
@SimpleVESA:
	MOV	DS:[DisplayPages],AL	  ;save number of pages available
	MOV	DI,OFFSET VESAbank        ;everything looks good
	MOV	DS:[BankSelectProc],DI    ;so init pointers
	MOV	DI,OFFSET VESAinit
	MOV	DS:[InitDisplayProc],DI
	INC	WORD PTR DS:[NumberModes] ;adj number modes
	MOV	BX,8		          ;Analog color display type 
	MOV	DS:[DisplayType],BX
	MOV	DI,OFFSET VESAname
	MOV	DS:[CardNamePtr],DI
	JMP	@DetectExit

@DefVesa13:
	MOV	DI,OFFSET VESAname	  ;it's vesa, but use
	MOV	DS:[CardNamePtr],DI	  ;std bios init
	MOV	DI,OFFSET VESAbank	  ;and default parms
	MOV	DS:[BankSelectProc],DI
	MOV	DI,OFFSET VGA320x200Init
	MOV	DS:[InitDisplayProc],DI
	CMP	WORD PTR DS:[NumberModes],0 ;fix up number modes
	JNZ	@DefVesaMode		    ;to one if needed
	MOV	AX,1
	MOV	DS:[NumberModes],AX     ;set number modes to 1
	JMP	@DefVesaMode

;Couldn't find VESA, so fall back on standard VGA
@NotVESA:
	MOV	DI,OFFSET VGABankSelect ;allow select mode 13H only
	MOV	DS:[BankSelectProc],DI
	MOV	DI,OFFSET VGA320x200Init
	MOV	DS:[InitDisplayProc],DI
	MOV	DI,OFFSET VGAcard
	MOV	DS:[CardNamePtr],DI
	MOV	AX,1
	MOV	DS:[NumberModes],AX     ;set number modes to 1

@DefVesaMode:
	MOV	BX,320		        ;set default scan line length
	MOV	DS:[ScanLineBytes],BX
	MOV	BX,64		        ;default window granularity = 64K
	MOV	DS:[WinGran],BX
	MOV	BX,0
	MOV	DS:[ModeSelect],BL      ;def select mode
	MOV	AX,1
	MOV	DS:[DisplayPages],AL	;set display pages to 1

	MOV	AX,1A00H
	INT	10H			;check if VGA type display
	CMP	AL,1AH
	JNZ	@NotVGA
	MOV	DS:[DisplayType],BX
	CMP	BL,6			;allow MCGA or VGA support
	JC	@NotVGA
	CMP	BL,0AH			;but not digital MCGA
	JZ	@NotVGA
	JMP	SHORT @DetectExit

;Couldn't find VGA/MCGA, so just error out
@NotVGA:
	MOV	AL,grNoInitGraph 	   ;mark as mode error
	MOV	DS:[ModeErrorFlag],AL
	MOV	DI,OFFSET DummyCard
	MOV	DS:[CardNamePtr],DI
	MOV	BYTE PTR DS:[ModeSelect],0 ;force mode to 0
@DetectExit:
	MOV	AL,DS:[ModeErrorFlag]	   ;return error status
	POP	BX
	POP	CX
	POP	DX
	POP	SI
	POP	DI
	POP	ES
	RET
DetectCard ENDP


;--------------------------------------------------
;code segment placment variables and stuff

VESAmodeSize EQU 6	;number of modes in table
VESAmodes:
	 DW 13H		;0: 320X200X256
	 DW 100H	;1: 640x400x256
	 DW 101H	;2: 640x480x256
	 DW 103H	;3: 800x600x256
	 DW 105H	;4: 1024x768x256
	 DW 107H	;5: 1280x1024x256

MaxModes EQU 6	;maximum available modes we can use

VESAname DB 7
	 DB 'VESA256',0		;VESA driver name

VGAcard	 DB 8
	 DB 'VGA/MCGA',0	;default VGA id


;----------------------------------------------------------------------
;Set screen mode to desired value
;Assume:  DS = data segment
;Enter:   mode to select in VidMode
;Return:  selected mode; Rets Z if selected, NZ if bad
;Destory: nothing

VESAinit PROC NEAR
	PUSH	BX
	MOV	BX,DS:[VidMode]
	OR	BH,DS:[ModeClearFlag]
	MOV	AX,04F02H
	INT	10H
;@@@	CALL	UpdateScanLineLength
	CMP	AX,004FH	;ret NZ if bad
	POP	BX
	RET
VESAinit ENDP

;----------------------------------------------------------------------
;select new display memory bank via Vesa calls
;Assume:  DS = data segment
;Enter:   DX = 64K bank to select
;Return:  Bank is selected 
;Destory: None

VESAbank PROC NEAR
	PUSH	AX
	PUSH	BX
	PUSH	DX
	MOV	AX,DS:[WinGran]
	MUL	DX
	MOV	DX,AX
	PUSH	DX
	MOV	AX,04F05H
	MOV	BX,0
	INT	10H
	POP	DX
	MOV	AX,04F05H
	MOV	BX,1
	INT	10H
	POP	DX
	POP	BX
	POP	AX
	RET
VESAbank ENDP


;----------------------------------------------------------------------
;select new display screen position within virtual display memory
;Assume:  DS = data segment
;Enter:   [ScreenOfsX] = X offset
;         [ScreenOfsY] = Y offset
;Return:  new display position is selected 
;Destory: None

SetScreenPos PROC NEAR
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	CMP	WORD PTR DS:[VidMode],0 ;if not Vesa, do nothing
	JZ	@SetScreenPosExit
        MOV	CX,[ScreenOfsX]   ;set new display X offset
        MOV	DX,[ScreenOfsY]   ;set new display Y offset
        MOV	AX,04F07H
        MOV	BX,0
        INT	10H               ;send the VESA command
	CMP	AX,004FH
	JZ	@SetScreenPosExit
	MOV	[ScreenOfsX],0	  ;if function not supported
	MOV	[ScreenOfsY],0	  ;return pos to 0,0
@SetScreenPosExit:
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET
SetScreenPos ENDP


;----------------------------------------------------------------------
;select new scan line length. This updates the virtual screen.
;You must call SetGraphMode() after using this function
;in order to initialize the BGI and graph unit variables to the new 
;display parameters. Note that this will change the display resolution.
;Assume:  DS = data segment
;Enter:   AX = New scan line length to use
;Return:  ScanLineBytes is updated is valid
;Destory: None

SetScanLineLength PROC NEAR
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	TEST	AX,0C000H	        ;invalid selection if high bits set
	JNZ	@SetScanLenError
	MOV	BX,DS:[StatBlockPtr]
	MOV	BX,DS:[BX].status.Defxres
	INC	BX
	CMP	AX,BX			;can't make line smaller than screen
	JNC	@SetScanLenLimit
	MOV	AX,BX
@SetScanLenLimit:
	MOV	CX,AX
	MOV	BX,0
	MOV	DX,0
	MOV	AX,4F06H
	INT	10H
	CMP	AX,004FH
	JNZ	@SetScanLenBad

	OR	BX,BX			;must be valid values returned
	JBE	@SetScanLenBad		;or the function is bad
	OR	DX,DX			;Hercules Stingray does not 
	JBE	@SetScanLenBad		;properly support this call,
	OR	CX,CX			;but says that it supports it.
	JBE	@SetScanLenBad

	MOV	byte ptr [VirtualFlag],1 ;1=virtual IS supported
	MOV	DS:[ScanLineBytes],BX
	MOV	DS:[VirtualWidth],CX
	MOV	DS:[VirtualHeight],DX
	MOV	AX,BX
	MUL	DX
	MOV	BX,1024
	DIV	BX
	INC	AX
	MOV	DS:[VidMemSize],AX
	JMP	@SetScanLenExit

@SetScanLenBad:
	MOV	CX,DS:[ScanLineBytes]	;attempt to restore original 
	MOV	BX,0			;scan line length
	MOV	AX,4F06H
	INT	10H
	MOV	byte ptr [VirtualFlag],0 ;0=virtual not supported

@SetScanLenExit:
	MOV	AX,DS:[ScanLineBytes]
	MOV	BX,DS:[StatBlockPtr]
	MOV	DS:[BX].status.VirtualScanBytes,AX
@SetScanLenError:
	MOV	BX,0FFFFH		;Init bank select reg
	MOV	DS:[PixelSegment],BX
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET
SetScanLineLength ENDP


;----------------------------------------------------------------------
;get current scan line length. 
;Assume:  DS = data segment
;Enter:   nothing
;Return:  ScanLineLength is updated
;Destory: None

;UpdateScanLineLength PROC NEAR
;	PUSH	ES
;	PUSH	DI
;	PUSH	AX
;	PUSH	BX
;	PUSH	CX
;	PUSH	DX
;
;	MOV	CX,DS:[VidMode]
;	MOV	AX,4F01H	    ;get mode info if available
;	MOV	DI,OFFSET TempWork  ;buffer of data is 
;	CALL	VESAInt		    ; returned in ES:DI
;	JC	@UpdateScanLenHard  ;if failed, get it via hardware
;	MOV	AX,ES:[DI].ModeInfoBlock.BytesPerScanLine
;	JMP	@UpdateScanLenExit  ;else read it from buffer
;
;@UpdateScanLenHard:
;	CLI
;	MOV	DX,3D4H
;	MOV	AL,13H
;	OUT	DX,AL
;	INC	DX
;	IN	AL,DX
;	STI
;	SHL	AX,1
;	SHL	AX,1
;	SHL	AX,1
;@UpdateScanLenExit:
;	MOV	DS:[ScanLineBytes],AX
;	POP	DX
;	POP	CX
;	POP	BX
;	POP	AX
;	POP	DI
;	POP	ES
;	RET
;UpdateScanLineLength ENDP


;----------------------------------------------------------------------
;get current screen info
;Assume:  DS = data segment
;Enter:   None
;Return:  ScanLineLength, VirtualPixels, VirtualLines are updated if valid
;Destory: None
;GetScreenInfo PROC NEAR
;	PUSH	AX
;	PUSH	BX
;	PUSH	CX
;	PUSH	DX
;	MOV	BX,1
;	MOV	AX,4F06H
;	INT	10H
;	CMP	AX,004FH
;	JNZ	@GetScreeninfoExit
;	MOV	DS:[ScanLineLength],BX
;	MOV	DS:[VirtualWidth],CX
;	MOV	DS:[VirtualHeight],DX
;@GetScreenInfoExit:
;	POP	DX
;	POP	CX
;	POP	BX
;	POP	AX
;	RET
;GetScreenInfo ENDP


;----------------------------------------------------------------------
;This processes the Vesa interupt $10 call to manage the protected
;verses real mode TSR calling problem with selectors via the DPMI
;Assume:  DS = data segment
;Enter:   registers as needed
;Return:  registers as needed    C=failed, NC=ok
;Destory: None

VESAInt:
	CMP DS:[CurOpMode],0  	;if CurOpMode is zero
	JNZ @VesaProt	   	;just manage the INT 10 call 
	PUSH DS
	POP ES  	   	;point ES:DI at temp buffer
	INT 10H		   	;normally
	CMP AX,004FH
	JZ @VESAintOK
	STC			;ret carry set if function fails
@VESAintOK:
	RET

;process VESA int $10 call via DPMI protected mode operation
@VesaProt:
	PUSH DS
	POP ES
	PUSH DI
	PUSH ES
	MOV word ptr DS:[SimInt.RealAX],AX ;pass command number 
	MOV word ptr DS:[SimInt.RealCX],CX ;vid mode where used
	MOV word ptr DS:[SimInt.RealDI],DI ;buf adr in DI
	MOV AX,DS:[RealModeDS]
	MOV DS:[SimInt.RealES],AX	;pass the real mode segment
	MOV DS:[SimInt.RealDS],AX
	MOV word ptr DS:[SimInt.RealSS],0	;let DPMI make it's own stack
	MOV word ptr DS:[SimInt.RealSP],0
	MOV word ptr DS:[SimInt.RealXX],0
	MOV word ptr DS:[SimInt.RealXX+2],0
	MOV DI,OFFSET SimInt
	MOV AX,0300H
	MOV BL,10H
	MOV BH,0
	MOV CX,0
	INT 31H
	POP ES
	POP DI		;returns ES:DI pointing at temp work 
	JC @VesaProtRet ;eek! dpmi call failed
	MOV AX,word ptr DS:[SimInt.RealAX]
	CMP AX,004FH    ;check on vesa call status
	JZ @VesaProtRet ;good, it made it
	STC		;urg. ret carry set on failure
@VesaProtRet:
	RET


;=====================================================================
