
;SHARD.INC - Copyright 1991-1995 Knight Software
; History:
;  17 May 1991 - first release
;  14 Jul 1991 - Corrected default line draw from MoveByte to ForeByte
;  20 Aug 1992 - fixed scan line length init problem 
;  22 Nov 1992 - Adapted for protected mode operation
;  17 Apr 1993 - Added Animation enhancement init code
;  25 Oct 1994 - Added display page offset to pixel addressing
;  25 Nov 1994 - Added line draw pixel count clr to miscinit
;                Added virtualBGI support code 
;  24 Jan 1995 - Inhibit VGA palette reload on mode change
;
;**********************************************************************
;Misc init stuff this is common VGA initialization code
;It sets up various variables and tables in the system
;Assume:  DS = data segment
;Entry:   [ModeSelect]=selected mode
;Return:  ES:BX points at selected status table
;	  AL = error status
;Destroys AH

MiscInit PROC NEAR
	PUSH	SI
	PUSH	DI
	PUSH	DX
	PUSH	CX

	MOV	BX,0FFFFH		;Init bank select reg
	MOV	DS:[PixelSegment],BX
	MOV	AL,0
	MOV	DS:[ModeXflag],AL	;clr modex flag

	MOV	AL,DS:[ModeSelect]
	AND	AX,7
	MOV	BX,offset StatusPtrTable;get mode status table ptr
	ADD	BX,AX			;to ES:SI
	ADD	BX,AX
	PUSH	DS
	POP	ES
	MOV	SI,DS:[BX]		;ES:SI = status table
	MOV	DS:[StatBlockPtr],SI	;save offset for later use
	MOV	AL,DS:[ModeErrorFlag]	;recover detect error result
	MOV	ES:[SI].status.stat,AL	;save detect error status

	MOV	BX,0
	MOV	DS:[ScreenOfsX],BX	;set cur disp X offset to zero
	MOV	DS:[ScreenOfsY],BX	;set cur disp Y offset to zero
	MOV	DS:[DrawOfsX],BX	;set cur draw X offset to zero
	MOV	DS:[DrawOfsY],BX	;set cur draw Y offset to zero

	MOV	BX,offset MoveByte
	MOV	DS:[FillPixelProc],BX		;preset proc ptrs
	MOV	DS:[PutImagePixelProc],BX
	MOV	DS:[GetImagePixelProc],BX
	MOV	BX,offset ForeByte
	MOV	DS:[LinePixelProc],BX
	MOV	DS:[TextPixelProc],BX
	MOV	AL,0
	MOV	DS:[FillPixelWritemode],AL	;preset mode selects
	MOV	DS:[GetImagePixelWritemode],AL
	MOV	DS:[PutImagePixelWritemode],AL
	MOV	AL,8
	MOV	DS:[LinePixelWritemode],AL
	MOV	DS:[TextPixelWritemode],AL
	MOV	BYTE PTR DS:[LinePixelCount],0 	;clr line draw pixel cnt

	MOV	AL,ES:[SI].status.ctblf		;init colors
	MOV	DS:[DrawForeColor],AL
	MOV	DS:[FillForeColor],AL
	MOV	AL,0
	MOV	DS:[DrawBackColor],AL
	MOV	DS:[PixelBackColor],AL
	MOV	DS:[FillBackColor],AL
	MOV	DS:[TextBackColor],AL
	MOV	DS:[GetImageBackColor],AL
	MOV	DS:[PutImageBackColor],AL

	CALL	GetTextInfo	;Load text info from BIOS

	MOV	AL,DS:[PaletteLoaded]
	OR	AL,AL		;don't load if pal load disabled
	JNZ	@MiscInit3

	MOV	AX,1012H
	MOV	BX,0		;Set default palette table
	MOV	CX,256
	MOV	DX,offset PaletteTable
	PUSH	DS
	POP	ES  	   	;point ES:DI at pal table
	CALL	GetSetPal

;	MOV	AX,1201H	;inhibit reload of palette
;	MOV	BL,31H		;on mode change
;	INT	10H
	
;	OR	BYTE PTR DS:[PaletteLoaded],01H

@MiscInit3:
	MOV	AL,DS:[ModeErrorFlag] ;return detect error result
	MOV	BX,SI		      ;ret ES:BX = status block
	POP	CX
	POP	DX
	POP	DI
	POP	SI
	RET
MiscInit ENDP


;**********************************************************************
;Misc init of stuff after switching hardware to the desired mode
;Assume: DS = data segment
;Entry:     nothing
;Return:    nothing
;destroies: nothing

PostInit PROC NEAR
	PUSH	SI
	PUSH	DX
	PUSH	BX
	PUSH	AX
	MOV	SI,DS:[StatBlockPtr]
	MOV	AX,DS:[SI].status.defxres
	INC	AX
	MOV	DS:[ScreenWidth],AX	;init phys scrn width for mode
	MOV	BX,DS:[SI].status.defyres
	INC	BX
	MOV	DS:[ScreenHeight],BX	;init phys scrn height for mode

	MOV	AX,DS:[SI].status.xres
	INC	AX
	MOV	DS:[DrawWidth],AX	;init drawing width for mode
	MOV	DS:[VirtualWidth],AX	;preinit virtual width
	MOV	BX,DS:[SI].status.yres
	INC	BX
	MOV	DS:[DrawHeight],BX	;init drawing height for mode
	MOV	DS:[VirtualHeight],BX	;preinit virtual height

	MOV	AX,DS:[SI].status.VirtualScanBytes ;post adj virtual screen
	CALL	SetScanLineLength	           ;to selected size
	MOV	AX,DS:[DrawWidth]
	MOV	BX,DS:[DrawHeight]	;post init drawing area
	CALL	SetDrawSize		;to selected size

	MOV	AX,DS:[VirtualHeight]
	XOR	DX,DX
	MOV	BX,DS:[DrawHeight]	;how many pages are there?
	DIV	BX
	MOV	DL,1			;at least one page is available
	OR	AX,AX
	JZ	@PostInit7
	MOV	DL,255			;max of 255 pages available
	OR	AH,AH
	JNZ	@PostInit7
	MOV	DL,AL
@PostInit7:
	MOV	DS:[DisplayPages],DL
@PostInitExit:
	POP	AX
	POP	BX
	POP	DX
	POP	SI
	RET	
PostInit ENDP


;**********************************************************************
;Create mode string from mode name and card name
;Assume: DS    = data segment
;Entry:  CL    = Mode number for name
;Return: ES:BX = pointer to mode string
;        NC = valid mode; C= bad mode number

GetModeName PROC NEAR
	PUSH	SI
	PUSH	DI
	PUSH	CX
	PUSH	DX
	MOV	DI,offset ModeNameBuffer
	INC	DI		    ;Point DI to target buffer
	MOV	CH,0
	MOV	DX,0	            ;init count to zero

	PUSH	DS	 	    ;ret ptr to a pascal string @ ES:BX
	POP	ES		    ;of mode name for mode passed in CX
	CMP	BYTE PTR DS:[ModeErrorFlag],grOK
	JZ	@GetModeName1	    ;is this mode bad?
	PUSH	CX
	MOV	SI,offset ModeNameBad
	MOV	CL,DS:[SI]	    ;get length
	ADD	DL,CL
	INC	SI
	CLD
	REP	MOVSB
	POP	CX

@GetModeName1:
	MOV	SI,offset ModeNameError ;Beyond mode number allowed?
	CMP	CX,DS:[NumberModes]
	JNC	@GetModeName2
	MOV	SI,offset ModeName
	MOV	AL,ModeNameSize     ;get table entry size
	MUL	CL
	ADD	SI,AX		    ;index into table for name

@GetModeName2:
	MOV	CL,DS:[SI]	    ;get mode name length
	INC	SI
	ADD	DL,CL		    ;save count
	CLD
	REP	MOVSB		    ;copy mode name to buffer

	MOV	SI,DS:[CardNamePtr] ;get card name buffer address
	MOV	CL,CS:[SI]	    ;get length of card name
	INC	SI		    ; (card name is in code segment)
	ADD	DL,CL		    ;add to overall name length
	CMP	DL,ModeNameMax      ;make sure mode name doesn't
	JC	@GetModeName3	    ;overflow buffer
	MOV	DL,ModeNameMax
@GetModeName3:
	MOV	AL,CS:[SI]
	INC	SI
	MOV	ES:[DI],AL	    ;add card name to mode name
	INC	DI
	LOOP	@GetModeName3
	MOV	BYTE PTR ES:[DI],0       ;tag with a null
	MOV	BX,offset ModeNameBuffer ;return pointer to name string 
	MOV	ES:[BX],DL	         ;set length

	POP	DX
	POP	CX
	POP	DI
	POP	SI
	RET
GetModeName ENDP


;---------------------------------------------------------------
;Default dummy card name (code segment placement)

DummyCard	DB 12
		DB 'Unknown card',0


;**********************************************************************
;Take the pixel position specified by PixelX, PixelY and the
;scan line length specified by BytesPerScanLine and compute
;the long address into the display. Selects proper segment bank
;on the display and stuffs bank select into PixelSegment and 
;the Display Offset address into PixelAddress. Returns how many 
;bytes are left in the display segment from the specified address
;in PixelSegmentLength.
;Assume:  DS       = data segment
;Entry:   [PixelX] = scan line offset (see description above)
;         [PixelY] = scan line number 
;Return:  [PixelAddress] = (see description above)
;Destroy: None

GetPixelAddress PROC NEAR
	PUSH	AX
	PUSH	DX
	PUSH	SI
	MOV	DX,DS:[DrawOfsY]        ;get display Y offset
	ADD	DX,DS:[PixelY]          ;add PixelY to it
	MOV	AX,DS:[ScanLineBytes]   ;bytes per scan line
	MUL	DX                      ;dx:ax=beginning of scan line
	MOV	SI,DS:[DrawOfsX]
	ADD	SI,DS:[PixelX]		;add display x offset to PixelX
	ADD	SI,AX		        ;si=address
	ADC	DX,0                    ;dx=bank
	CMP	DS:[PixelSegment],DX    ;set the bank select
	JZ	@DoneBankSelect
	MOV	DS:[PixelSegment],DX
	CALL	DS:[BankSelectProc]     ;call approprate bank sel code
@DoneBankSelect:
	MOV	DS:[PixelAddress],SI    ;save the pixel address
	NOT	SI
	INC	SI
	JNZ	@BankSelectOK
	DEC	SI		           ;limit length to 65535
@BankSelectOK:
	MOV	DS:[PixelSegmentLength],SI ;save the segment length
	POP	SI
	POP	DX
	POP	AX
	RET
GetPixelAddress ENDP


;**********************************************************************
;Standard VGA only has 320x200 which fully fits in 64K bank.
;Thus no selection needs to be done here.
;Assume:  DS = Nothing
;Entry:   N/A
;Return:  N/A
;Destroy: None

VGABankSelect PROC NEAR
	RET
VGABankSelect ENDP


;----------------------------------------------------------------------
;Select standard VGA320x200 mode operation
;Assume:  DS = data segment
;Entry:   N/A
;Return:  Correct display mode selected
;Destroy: AX

VGA320x200Init PROC NEAR
	MOV	AX,0013H	;select standard mode
	OR	AL,DS:[ModeClearFlag]
	INT	10H		;call BIOS to set the mode
	MOV	DS:[ScanLineBytes],320
	XOR	AX,AX		;ret Zero means all ok
	RET
VGA320x200Init ENDP


;----------------------------------------------------------------------
;Set EGA Pal color register in AL
;Assume:  DS = data segment
;Entry:   AL = color index
;         BL = color number
;Return:  palette is updated
;Destroy: AX,BX

SetEGAPal PROC NEAR
	PUSH	ES
	PUSH	DI
	PUSH	CX
	PUSH	BX
	PUSH	AX

;	XOR	AH,AH
;	AND	AL,0FH
;	MOV	DI,offset EGApalette ;copy the palette stuff
;	INC	DI
;	ADD	DI,AX
;	MOV	DS:[DI],BL
;
;	PUSH	DS
;	POP	ES
;	MOV	DX,offset EGApalette
;	INC	DX
;	MOV	AX,1002H
;	CALL	GetSetPal

	MOV	BH,BL	;set color palette
	MOV	BL,AL
	MOV	AX,1000H
	INT	10H

	POP	AX
	POP	BX
	POP	CX
	POP	DI
	POP	ES
	RET
SetEGAPal ENDP


;----------------------------------------------------------------------
;Set full EGA color palette
;Note: although the manual says that the overscan register is not
;set with this function, in reality, it is passed in the array
;(it appears to be passed as a zero value). BI's SetAllPalette
;works like this too (ie it passed the overscan value at the end
;of the palette array). Thus there are actually 17 bytes passed.
;Note that while the GetDefaultPalette function (see Query Color)
;requires a length byte in the start of the array, this function is 
;intended to be feed to the BIOS directly, so there is no length
;byte. It is assumed that the array size is already known by the 
;mode you are currently in. I only support full EGA palette here.
;Assume:  DS    = data segment
;Entry:   ES:BX = points to palette table data
;Return:  palette is updated
;Destroy: AX,BX,DX

SetFullEGAPal PROC NEAR
	PUSH	ES
	PUSH	DI
	PUSH	CX
	PUSH	BX
	PUSH	AX

	MOV	DI,offset EGApalette ;copy the palette stuff
	INC	DI
	MOV	CX,17		     ;to local memory
@EGAPalCopy:
	MOV	AL,ES:[BX]
	MOV	DS:[DI],AL
	INC	DI
	INC	BX
	LOOP	@EGAPalCopy

	PUSH	DS
	POP	ES
	MOV	DX,offset EGApalette
	INC	DX
	MOV	AX,1002H
	CALL	GetSetPal

	POP	AX
	POP	BX
	POP	CX
	POP	DI
	POP	ES
	RET
SetFullEGAPal ENDP


;----------------------------------------------------------------------
;Set VGA Pal color register in AX
;Assume:  DS = data segment
;Entry:   AX = color index
;         BX = red
;         CX = green
;	  DX = blue
;Return:  palette is updated
;Destroy: AX,BX,CX,DX

SetVGAPal PROC NEAR
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	MOV	CH,CL	;set an individual DAC color register
	MOV	CL,DL	;BX=red, CX=green, DX=blue, AX=index
	MOV	DH,BL	;top two bits of AX = 10
	MOV	DL,0
	MOV	BX,AX
	AND	BX,00FFH
	MOV	AX,1010H
	INT	10H
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET
SetVGAPal ENDP


;----------------------------------------------------------------------
;process Pal set int $10 call via DPMI protected mode operation
;Assume:  DS = data segment
;Entry:   AX = command
;         BX = pal start index
;         CX = items to change (count)
;Destroy: All

GetSetPal PROC NEAR
	CMP	DS:[CurOpMode],0  	;if CurOpMode is zero
	JNZ	@PalProt	   	;just do a real INT 10 call 

	INT	10H
	RET

@PalProt:
	MOV word ptr DS:[SimInt.RealAX],AX ;pass command number 
	MOV word ptr DS:[SimInt.RealBX],BX
	MOV word ptr DS:[SimInt.RealCX],CX
	MOV word ptr DS:[SimInt.RealDX],DX
	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 AX,DS
	MOV ES,AX
	MOV DI,offset SimInt	;go simulate the interrupt call
	MOV AX,0300H
	MOV BL,10H
	MOV BH,0
	MOV CX,0
	INT 31H
	RET
GetSetPal ENDP


;----------------------------------------------------------------------
;select new display page 
;Assume:  DS = data segment
;Enter:   AL = display page to select 
;Return:  Display page is selected 
;Destroy: None

SetDisplayPage PROC NEAR
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	MOV	AH,0		        ;compute new display Y offset
	MOV	DX,DS:[DrawHeight]
	MUL	DX
	MOV	DX,DS:[VirtualHeight]
	SUB	DX,DS:[DrawHeight]
	JC	@SetDispPg1
	CMP	AX,DX			;don't allow past end of mem
	JNA	@SetDispPg2
@SetDispPg1:
	MOV	AX,0
@SetDispPg2:
	MOV	DS:[ScreenOfsY],AX      ;save result
	CALL	SetScreenPos		;set the new position
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET
SetDisplayPage ENDP


;----------------------------------------------------------------------
;select new drawing page 
;Assume:  DS = data segment
;Enter:   AL = drawing page to select
;Return:  Drawing page is selected 
;Destroy: None

SetDrawingPage PROC NEAR
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	MOV	AH,0		        ;compute new page Y offset
	MOV	DX,DS:[DrawHeight]
	MUL	DX
	MOV	DX,DS:[VirtualHeight]
	SUB	DX,DS:[DrawHeight]
	JC	@SetDrawPg1
	CMP	AX,DX			;don't allow past end of mem
	JNA	@SetDrawPg2
@SetDrawPg1:
	MOV	AX,0
@SetDrawPg2:
	MOV	DS:[DrawOfsY],AX        ;save result
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET
SetDrawingPage ENDP


;----------------------------------------------------------------------
;set screen offset position on virtual display
;Assume:  DS = data segment
;Enter:   AX=value to set for Screen X offset
;         BX=value to set for Screen Y offset
;Return:  Screen offset updated if valid
;Destroy: None

SetScreenOfs PROC NEAR
	PUSH	SI
	PUSH	DX
	PUSH	BX
	PUSH	AX
	TEST	AX,0C000H	           ;invalid selection if high bits set
	JNZ	@SetScreenOfsExit
	TEST	BX,0C000H	           ;invalid selection if high bits set
	JNZ	@SetScreenOfsExit

	MOV	SI,DS:[StatBlockPtr]
	MOV	DX,DS:[VirtualWidth]
	DEC	DX
	SUB	DX,DS:[SI].status.Defxres
	JC	@SetScreenOfsExit
	CMP	AX,DX
	JBE	@SetScreenOfsXLim
	MOV	AX,DX
@SetScreenOfsXLim:
	MOV	DX,DS:[VirtualHeight]
	DEC	DX
	SUB	DX,DS:[SI].status.Defyres
	JC	@SetScreenOfsExit
	CMP	BX,DX
	JBE	@SetScreenOfsYlim
	MOV	BX,DX
@SetScreenOfsYlim:
	MOV	DS:[ScreenOfsX],AX         ;screen display X start offset
	MOV	DS:[ScreenOfsY],BX         ;screen display Y start offset
@SetScreenOfsExit:
	CALL	SetScreenPos		   ;set the display hardware
	POP	AX
	POP	BX
	POP	DX
	POP	SI
	RET
SetScreenOfs ENDP


;----------------------------------------------------------------------
;set drawing offset position on virtual display
;Assume:  DS = data segment
;Enter:   AX=value to set for drawing X offset
;         BX=value to set for drawing Y offset
;Return:  Drawing offset updated if valid
;Destroy: None

SetDrawOfs PROC NEAR
	PUSH	DX
	PUSH	BX
	PUSH	AX
	TEST	AX,0C000H	           ;invalid selection if high bits set
	JNZ	@SetDrawOfsExit
	TEST	BX,0C000H	           ;invalid selection if high bits set
	JNZ	@SetDrawOfsExit

	MOV	DX,DS:[VirtualWidth]
	SUB	DX,DS:[DrawWidth]
	JC	@SetDrawOfsExit
	CMP	AX,DX
	JBE	@SetDrawOfsXlim		   ;limit Xofs to max allowed
	MOV	AX,DX
@SetDrawOfsXlim:
	MOV	DX,DS:[VirtualHeight]
	SUB	DX,DS:[DrawHeight]
	JC	@SetDrawOfsExit
	CMP	BX,DX
	JBE	@SetDrawOfsYlim		   ;limit Yofs to max allowed
	MOV	BX,DX
@SetDrawOfsYlim:
	MOV	DS:[DrawOfsX],AX           ;drawing memory X start offset
	MOV	DS:[DrawOfsY],BX           ;drawing memory Y start offset
@SetDrawOfsExit:
	POP	AX
	POP	BX
	POP	DX
	RET
SetDrawOfs ENDP



;----------------------------------------------------------------------
;set drawing area size on the virtual display
;Assume:  DS = data segment
;Enter:   AX=value to set for drawing width (X) in pixels
;         BX=value to set for drawing height (Y) in scan lines
;Return:  Drawing offset updated if valid
;Destroy: None

SetDrawSize PROC NEAR	;* call SetGraphMode after using this *
	PUSH	SI
	PUSH	DX
	PUSH	BX
	PUSH	AX
	TEST	AX,0C000H	           ;invalid selection if high bits set
	JNZ	@SetDrawSizeExit
	TEST	BX,0C000H	           ;invalid selection if high bits set
	JNZ	@SetDrawSizeExit

	MOV	DX,DS:[VirtualWidth]
	SUB	DX,DS:[DrawOfsX]
	JC	@SetDrawSizeExit
	CMP	AX,DX
	JBE	@SetDrawWidthLim
	MOV	AX,DX			   ;limit width to max allowed
@SetDrawWidthLim:
	MOV	DX,DS:[VirtualHeight]
	SUB	DX,DS:[DrawOfsY]
	JC	@SetDrawSizeExit
	CMP	BX,DX
	JBE	@SetDrawHeightLim
	MOV	BX,DX			   ;limit height to max allowed
@SetDrawHeightLim:

	MOV	SI,DS:[StatBlockPtr]
	MOV	DS:[DrawWidth],AX          ;drawing memory X start offset
	DEC	AX
	MOV	DS:[SI].status.xres,AX	   ;save new x res
	MOV	DS:[SI].status.xefres,AX   ;save new x ef res
	INC	AX
	SHR	AX,1
	SHR	AX,1			   ;predivide by 4
	MOV	DX,DS:[SI].status.DeviceXmul
	MUL	DX
	MOV	DS:[SI].status.xinch,AX	   ;save new x size

	MOV	AX,BX
	MOV	DS:[DrawHeight],AX         ;drawing memory Y start offset
	DEC	AX
	MOV	DS:[SI].status.yres,AX	   ;save new y res
	MOV	DS:[SI].status.yefres,AX   ;save new y ef res
	INC	AX
	SHR	AX,1
	SHR	AX,1			   ;predivide by 4
	MOV	DX,DS:[SI].status.DeviceYmul
	MUL	DX
	MOV	DS:[SI].status.yinch,AX	   ;save new y size
@SetDrawSizeExit:
	POP	AX
	POP	BX
	POP	DX
	POP	SI
	RET
SetDrawSize ENDP

;----------------------------------------------------------------------
;zero screen and drawing area size on the virtual display
;Assume:  DS = data segment
;Enter:   nothing 
;Return:  screen and drawing area set to 0,0; 
;         drawing size = screen size
;Destroy: None

ZeroDisplay PROC NEAR  ;* call SetGraphMode after using this *
	PUSH	SI
	PUSH	BX
	PUSH	AX
	MOV	AX,0
	MOV	DS:[ScreenOfsX],AX	;set cur screen X offset to zero
	MOV	DS:[ScreenOfsY],AX	;set cur screen Y offset to zero
	CALL	SetScreenPos
	MOV	AX,0
	MOV	DS:[DrawOfsX],AX	;set cur draw X offset to zero
	MOV	DS:[DrawOfsY],AX	;set cur draw Y offset to zero

	MOV	SI,DS:[StatBlockPtr]
	MOV	AX,DS:[SI].status.Defxres
	INC	AX
	MOV	DS:[DrawWidth],AX	;init drawing width for mode
	MOV	BX,DS:[SI].status.Defyres
	INC	BX
	MOV	DS:[DrawHeight],BX	;init drawing height for mode
	CALL	SetDrawSize
	POP	AX
	POP	BX
	POP	SI
	RET
ZeroDisplay ENDP


;----------------------------------------------------------------------


