		COMMENT %

	GRAPHIC WORKSHOP Tseng ET3000/4000 SVGA screen driver
	COPYRIGHT (C) 1990 ALCHEMY MINDWORKS INC.
	VERSION 1.1

	portions copyright (C) 1990 Gregory D. Weeks
	permision granted for Alchemy Mindworks to distribute.

	This driver was adapted from the skeletal driver
	supplied with graphic workshop 3.4.  It will drive
	Tseng ET3000 and ET4000 equiped SVGA cards.  The Tseng
	cards are capable of up to 1024x768 in 256 colors.  This
	driver must be installed after assembly for the
	moniter type and memory size on the card, as well as
	for the chip type on the card.

	This driver is designed to be assembled with A86.


		%

VERSION		EQU	1		;VERSION NUMBER
SUBVERSION	EQU	1		;SUBVERSION NUMBER

_AOFF		EQU	6		;STACK OFFSET

; these are the maximum possible screen sizes
TLI_WIDE        EQU     1024    ;maximum screen width in pixels
TLI_DEEP        EQU     768     ;maximum screen depth in lines
TLI_SCREENSEG   EQU     0A000H  ;segment of tseng card video buffer
TLI_BYTES       EQU     128     ;maximum width of planar modes in bytes

GDC_seg_sel equ 03cdh

;THIS MACRO SELECTS AN EGA PLANE
EGAPLANE        MACRO
		MOV	AL,2
		MOV	DX,03C4H
		OUT	DX,AL
		INC	DX
		MOV     AL,#1
		OUT	DX,AL
		#EM

		ORG     0000H           ;ORIGIN FOR LOADABLE DRIVER

		DB	'ALCHDRV2'	;SIGNATURE - DON'T CHANGE THIS

;THE FOLLOWING ARE THE POINTERS TO THE CALLABLE ROUTINES AND THE COMMON
;DATA. THE SEGMENTS ARE FILLED IN BY GRAPHIC WORKSHOP. DON'T CHANGE ANYTHING.
DISPATCH:
		DW      VGA_on          ;FAR POINTER TO VGA MODE SELECT
		DW      ?
		DW      VGA_line        ;FAR POINTER TO VGA LINE DISPLAY
		DW	?
		DW      VGA_off         ;FAR POINTER TO VGA MODE DESELECT
		DW	?
		DW	VGA_PALETTE	;FAR POINTER TO VGA PALETTE SET
		DW	?
		DW	VGA_OVERSCAN	;FAR POINTER TO VGA OVERSCAN SET
		DW	?
		DW      EGA_on          ;FAR POINTER TO EGA MODE SELECT
		DW      ?
		DW	EGA_LINE	;FAR POINTER TO EGA LINE DISPLAY
		DW	?
		DW      EGA_off         ;FAR POINTER TO EGA MODE DESELECT
		DW	?
		DW	EGA_PALETTE	;FAR POINTER TO EGA PALETTE SET
		DW	?
		DW      MONO_on         ;FAR POINTER TO MONO MODE SELECT
		DW      ?
		DW	MONO_FRAME	;FAR POINTER TO MONO PAGE DISPLAY
		DW	?
		DW	MONO_LINE	;FAR POINTER TO MONO LINE DISPLAY
		DW	?
		DW      MONO_off        ;FAR POINTER TO MONO MODE DESELECT
		DW	?
		DW	0,0		;NULL ONE
		DW	0,0		;NULL TWO
		DW	0,0		;NULL THREE
		DW	0,0		;NULL FOUR

V_VGAWIDE       DW      TLI_WIDE        ;VGA SCREEN WIDTH
V_VGADEEP       DW      TLI_DEEP        ;VGA SCREEN DEPTH
V_VGASCRNSEG    DW      TLI_SCREENSEG   ;VGA SCREEN SEGMENT
V_EGAWIDE       DW      TLI_WIDE        ;EGA SCREEN WIDTH
V_EGADEEP       DW      TLI_DEEP        ;EGA SCREEN DEPTH
V_EGABYTES      DW      TLI_BYTES       ;EGA SCREEN BYTES
V_EGASCRNSEG    DW      TLI_SCREENSEG   ;EGA SCREEN SEGMENT
V_MONOWIDE      DW      TLI_WIDE       ;MONO SCREEN WIDTH
V_MONODEEP      DW      TLI_DEEP       ;MONO SCREEN DEPTH
V_MONOBYTES     DW      TLI_BYTES      ;BYTE WIDTH ON MONOCHROME SCREEN
V_MONOSCRNSEG   DW      TLI_SCREENSEG  ;MONOCHROME SCREEN SEGMENT

;THESE VERSION NUMBERS REFLECT THE DRIVER TEMPLATE VERSION AND THE
;VERSION OF THE DRIVER ITSELF. YOU CAN CHANGE THE SUBVERSION VALUE
;TO REFLECT CHANGES IN YOUR DRIVER. THE VERSION VALUE MUST REMAIN
;UNCHANGED OR GRAPHIC WORKSHOP MAY REJECT YOUR DRIVER.
		DW	VERSION
		DW	SUBVERSION

;THE DESCRIPTION APPEARS IN THE F10 "ABOUT" BOX IN GRAPHIC
;WORKSHOP WHEN AN EXTERNAL DRIVER IS BEING USED. IT CAN'T
;EXCEED 24 CHARACTERS AND MUST BE NULL TERMINATED
   DB      'Tseng ET3000/4000',0

Installed dw 00302h  ; moniter,memory - descriptions at end of file
	  dw VGA_mode

;THIS ROUTINE SELECTS THE VGA 256 COLOUR MODE
;THE HEIGHT AND WIDTH OF THE IMAGE ARE ON THE STACK - THESE
;MAY BE USEFUL IF YOU WANT TO PICK ONE OF SEVERAL AVAILABLE
;MODES BASED ON THE AREA OF THE PICTURE TO BE DISPLAYED
VGA_on:
   push bp
   mov bp,sp
   push ds
   mov ds,cs

   mov ax,[bp+_AOFF]   ; cols
   mov bx,[bp+_AOFF+2] ; rows
   mov si,VGA_mode
   call pick_mode
   mov V_VGAWIDE,ax
   mov V_VGADEEP,bx

   mov ax,cx
   int 10h

   mov cx,V_VGADEEP     ;depth of screen in the current mode
   sub dx,dx
   mov bx,dx
   mov si,SCREENTABLE
   mov di,Row_seg
   mov [si],dx
   mov [di],bl
   or b[di],40h
   add si,2
   inc di
   dec cx
l2:
   add dx,V_VGAWIDE
   jnc >l1
   inc bl
   or b[di-1],80h  ; set a flag to show that the segment changed in the
l1:                ; last line.
   mov [si],dx
   mov [di],bl
   add si,2
   inc di
   loop l2

   pop ds,bp
   retf


;THIS ROUTINE DISPLAYS A VGA LINE
;THE FIRST ARGUMENT ON THE STACK (2 WORDS) IS A FAR POINTER TO
;THE LINE. THE SECOND ARGUMENT IS THE LENGTH OF THE LINE IN PIXELS
VGA_line:
   push bp
   mov bp,sp
   push ds
   push es

   mov si,[bp + _AOFF + 0]     ;OFFSET OF SOURCE
   mov ds,[bp + _AOFF + 2]     ;SEGMENT OF SOURCE

   mov bx,[bp + _AOFF + 6]     ;GET LINE NUMBER
   cmp bx,cs:V_VGADEEP
   jge VGA_line_exit

   mov al,cs:[Row_seg + bx]
   shl bx,1
   mov di,cs:[screentable + bx]
   cld
   mov cx,[bp + _AOFF + 4]
   cmp cx,0
   je VGA_line_exit
   cmp cx,cs:V_VGAWIDE
   jl >l1
   mov cx,cs:V_VGAWIDE
l1:
   test al,80h  ; test flag for segment change
   jnz > l2
   mov dx,GDC_seg_sel
   out dx,al
   mov ax,cs:V_VGASCRNSEG
   mov es,ax
   shr cx,1       ;convert to word moves
   jc >l3
   rep movsw
   jmp >l4
l3:
   rep movsw
   movsb
l4:
   jmp VGA_line_exit
; from here down is for the few lines that cross segment
; bounday's.  This routine is slower than the previous rep movsw's.
l2:
   mov dx,GDC_seg_sel
   and al,7fh
   out dx,al
   mov bl,al
   mov ax,cs:V_VGASCRNSEG
   mov es,ax
l1:
   movsw
   cmp di,0
   jne >l2
   mov al,bl
   inc al
   out dx,al
l2:
   dec cx
   cmp cx,2
   je >l5
   loop l1
   jmp >l3
l5:
   movsb
l3:

VGA_line_exit:
   pop es,ds,bp
   retf


;THIS ROUTINE SETS THE VGA PALETTE
;THE FIRST ARGUMENT ON THE STACK IS A FAR POINTER TO
;THE PALETTE DATA. THE SECOND ARGUMENT IS THE NUMBER OF COLOURS.
VGA_PALETTE:
		PUSH	BP
		MOV	BP,SP
		PUSH	DS

		MOV	SI,[BP + _AOFF + 0]	;OFFSET OF SOURCE
		MOV	DS,[BP + _AOFF + 2]	;SEGMENT OF SOURCE

		MOV	CX,[BP + _AOFF + 4]	;NUMBER OF COLOURS

                CMP	CX,0			;CHECK FOR NASTIES
		JG	GVP0
		JMP	GVPX

		;WE'LL SET THE PALLETTE USING DIRECT REGISTERS RATHER
		;THAN A BIOS CALL AS IT LOOKS NICER

GVP0:		MOV	DX,03C6H
		MOV	AL,0FFH
		OUT	DX,AL

		MOV	BX,0

GVP1:		PUSH	CX
		MOV	DX,03C8H
		MOV	AL,BL
		INC	BX
		OUT	DX,AL

		INC	DX

		LODSB
		SHR	AL,1
		SHR	AL,1
		OUT	DX,AL

		LODSB
		SHR	AL,1
		SHR	AL,1
		OUT	DX,AL

		LODSB
		SHR	AL,1
		SHR	AL,1
		OUT	DX,AL

		POP	CX
		LOOP    GVP1


GVPX:
		POP	DS
		POP	BP
		RETF

;THIS ROUTINE SETS THE VGA OVERSCAN.
;THE FIRST STACK ARGUMENT IS THE COLOUR NUMBER.
VGA_OVERSCAN:
		PUSH    BP
		MOV	BP,SP
		MOV	AX,1001H
		MOV	BX,[BP + _AOFF + 0]
		XCHG	BH,BL
		INT	10H
		POP	BP
		RETF

;THIS ROUTINE SELECTS THE EGA 16 COLOUR MODE
;THE HEIGHT AND WIDTH OF THE IMAGE ARE ON THE STACK - THESE
;MAY BE USEFUL IF YOU WANT TO PICK ONE OF SEVERAL AVAILABLE
;MODES BASED ON THE AREA OF THE PICTURE TO BE DISPLAYED
EGA_on:
   push bp
   mov bp,sp
   push ds
   mov ds,cs

   mov ax,[bp+_AOFF]   ; cols
   mov bx,[bp+_AOFF+2] ; rows
   mov si,EGA_mode
   call pick_mode
   mov V_EGAWIDE,ax
   mov V_EGADEEP,bx

   add ax,7
   shr ax,1
   shr ax,1
   shr ax,1
   mov V_EGABYTES,AX

   mov ax,cx
   int 10h

   mov cx,V_EGADEEP     ;depth of screen in the current mode
   sub dx,dx
   mov bx,dx
   mov si,SCREENTABLE
   mov di,Row_seg
   mov [si],dx
   mov [di],bl
   or b[di],40h
   add si,2
   inc di
   dec cx
l2:
   add dx,V_EGABYTES
   jnc >l1
   inc bl
l1:
   mov [si],dx
   mov [di],bl
   add si,2
   inc di
   loop l2

   pop ds,bp
   retf


;THIS ROUTINE DISPLAYS AN EGA LINE
;THE FIRST ARGUMENT ON THE STACK (2 WORDS) IS A FAR POINTER TO
;THE LINE. THE SECOND ARGUMENT IS THE LENGTH OF THE LINE IN BYTES
EGA_LINE:
		PUSH	BP
		MOV	BP,SP
		PUSH	DS
		PUSH	ES

		MOV	SI,[BP + _AOFF + 0]	;OFFSET OF SOURCE
		MOV	DS,[BP + _AOFF + 2]	;SEGMENT OF SOURCE
		MOV     BX,[BP + _AOFF + 6]	;GET LINE NUMBER
		CMP     BX,cs:V_EGADEEP
   if GE jmp long  SHOWEGAX

   mov al,cs:[Row_seg+bx]
   mov dx,GDC_seg_sel
   out dx,al

		SHL	BX,1
		MOV	DI,CS:[SCREENTABLE+BX]

		MOV	AX,0A000H
		MOV	ES,AX
		MOV     BX,[BP + _AOFF + 4]     ;LENGTH OF MOVE IN BYTES

   shr bx,1  ; convert to word moves
   jc >l3
		MOV     CX,BX       ; this part does an even # of bytes
		EGAPLANE	1
		CLD
		PUSH	DI
   rep movsw
		POP	DI

		MOV	CX,BX
		EGAPLANE	2
		PUSH	DI
   rep movsw
		POP	DI

		MOV	CX,BX
		EGAPLANE	4
		PUSH	DI
   rep movsw
		POP	DI

		MOV	CX,BX
		EGAPLANE	8
		PUSH	DI
   rep movsw
		POP	DI
		EGAPLANE        0FH
   jmp >l4
l3:
		MOV     CX,BX       ; this part does an odd # of bytes
		EGAPLANE	1
		CLD
		PUSH	DI
   rep movsw
   movsb
		POP	DI

		MOV	CX,BX
		EGAPLANE	2
		PUSH	DI
   rep movsw
   movsb
		POP	DI

		MOV	CX,BX
		EGAPLANE	4
		PUSH	DI
   rep movsw
   movsb
		POP	DI

		MOV	CX,BX
		EGAPLANE	8
		PUSH	DI
   rep movsw
   movsb
		POP	DI
		EGAPLANE        0FH
l4:

SHOWEGAX:	POP	ES
		POP	DS
		POP	BP
		RETF

;THIS ROUTINE SETS THE EGA PALETTE
;THE FIRST ARGUMENT ON THE STACK IS A FAR POINTER TO
;THE PALETTE DATA. THE SECOND ARGUMENT IS THE NUMBER OF COLOURS.
EGA_PALETTE:
		PUSH	BP
		MOV	BP,SP
		PUSH	DS

		MOV	SI,[BP + _AOFF + 0]	;OFFSET OF SOURCE
		MOV	DS,[BP + _AOFF + 2]	;SEGMENT OF SOURCE

		MOV	CX,[BP + _AOFF + 4]	;NUMBER OF COLOURS
		SUB	BX,BX
                CMP	CX,16
		JLE	EGA_PALETTE1
		MOV	CX,16

EGA_PALETTE1:	MOV	BH,[SI]
		MOV	AX,1000H
		INT	10H
		INC	BL
		INC	SI
		LOOP	EGA_PALETTE1

		POP	DS
		POP	BP
		RETF


;THIS ROUTINE SELECTS THE 2 COLOUR MODE
;THE HEIGHT AND WIDTH OF THE IMAGE ARE ON THE STACK - THESE
;MAY BE USEFUL IF YOU WANT TO PICK ONE OF SEVERAL AVAILABLE
;MODES BASED ON THE AREA OF THE PICTURE TO BE DISPLAYED
MONO_on:
   push bp
   mov bp,sp
   push ds
   mov ds,cs

   mov ax,[bp+_AOFF]   ; cols
   mov bx,[bp+_AOFF+2] ; rows
   mov si,MONO_mode
   call pick_mode
   mov V_MONOWIDE,ax
   mov V_MONODEEP,bx

   add ax,7
   shr ax,1
   shr ax,1
   shr ax,1
   mov V_MONOBYTES,AX

   mov ax,cx
   int 10h

   mov cx,V_MONODEEP     ;depth of screen in the current mode
   sub dx,dx
   mov bx,dx
   mov si,SCREENTABLE
   mov di,Row_seg
   mov [si],dx
   mov [di],bl
   or b[di],40h
   add si,2
   inc di
   dec cx
l2:
   add dx,V_MONOBYTES
   jnc >l1
   inc bl
l1:
   mov [si],dx
   mov [di],bl
   add si,2
   inc di
   loop l2

   pop ds,bp
   retf



UPDATE_MOVE	EQU	2
UPDATE_PAD	EQU	4
UPDATE_ADJUST	EQU	6

;THIS ROUTINE DISPLAYS A FULL MONOCHROME PAGE
;THE FIRST ARGUMENT ON THE STACK IS A FAR POINTER TO THE PAGE
;THE SECOND ARGUMENT IS THE WIDTH OF THE BITMAP (IN BYTES)
;THE THIRD ARGUMENT IS THE NUMBER OF LINES TO DISPLAY
;NOTE: THE SOURCE BUFFER MAY BE BIGGER THAN 64K.
MONO_FRAME:
		PUSH	BP
		MOV	BP,SP
		SUB	SP,UPDATE_ADJUST
		PUSH	DS
		PUSH	ES

		MOV     AX,cs:V_MONOSCRNSEG       ;POINT TO THE SCREEN
		MOV	ES,AX
		MOV	AX,[BP + _AOFF + 4]    	;GET THE WIDTH OF MOVE
		MOV	[BP - UPDATE_MOVE],AX	;SAVE IT LOCALLY
		MOV	WORD PTR [BP - UPDATE_PAD],0	;SET ADJUSTMENT

		CMP     AX,cs:V_MONOBYTES           ;IF THE MOVE IS LESS THAN
		JL	UPDATE0			;SCREEN WIDTH, GO FOR IT

		SUB     AX,cs:V_MONOBYTES           ;ELSE, SET MOVE WIDTH
		MOV	[BP - UPDATE_PAD],AX	;...AND THE AMOUNT TO

		MOV     AX,cs:V_MONOBYTES           ;...ADJUST THE POINTER
		MOV	[BP - UPDATE_MOVE],AX	;...AFTER EACH LINE

UPDATE0:	MOV	SI,[BP + _AOFF + 0]	;OFFSET OF BITMAP
		MOV	DS,[BP + _AOFF + 2]	;SEGMENT OF BITMAP
		MOV	CX,[BP + _AOFF + 6]	;NUMBER OF LINES

		CLD				;CLEAR DIRECTION FLAG
		SUB	BX,BX

UPDATE1:        PUSH    CX                      ;SAVE COUNT (LINE NUMBER)

   shr bx,1                ;select the proper segment for the line
   mov al,cs:[Row_seg+bx]  ; actually only needed for the 1024x768 mode
   shl bx,1                ; but it's only done once per line and it's
   mov dx,GDC_seg_sel      ; fairly quick so I didn't add the extra code
   out dx,al               ; to bypass it in the other modes

		MOV	DI,CS:[SCREENTABLE + BX]
		ADD	BX,2			;POINT TO NEXT LINE

		MOV     CX,[BP - UPDATE_MOVE]   ;GET THE MOVE SIZE

   shr cx,1  ; convert to word moves
   jc >l3
   rep movsw
   jmp >l4
l3:
   rep movsw
   movsb
l4:

		ADD	SI,[BP - UPDATE_PAD]	;ADJUST THE POINTER

		CMP	SI,0F800H		;ARE WE WITHIN 2K OF TOP?
		JL      UPDATE2			;IF NOT, CARRY ON

		MOV	AX,SI			;SEE HOW MANY SEGMENTS ARE
		MOV	CL,4			;...IN SI (SI DIV 4)
		SHR	AX,CL

		MOV	CX,DS			;ADD THEM TO THE DATA SEGMENT
		ADD	CX,AX			;...(YOU CAN'T JUST ADD DS,AX)
		MOV	DS,CX
		AND	SI,000FH		;ADJUST SI (SI MOD 16)

UPDATE2:	POP	CX			;GET COUNT BACK
		LOOP	UPDATE1                	;DECREMENT AND LOOP

		POP	ES
		POP	DS

		ADD	SP,UPDATE_ADJUST
		POP	BP

		RETF

;THIS ROUTINE DISPLAYS A SINGLE MONOCHROME LINE
;THE FIRST ARGUMENT ON THE STACK IS A FAR POINTER TO THE LINE
;THE SECOND ARGUMENT IS THE LINE NUMBER
;THE THIRD ARGUMENT IS THE WIDTH OF THE BITMAP (IN BYTES)
MONO_LINE:
		PUSH	BP
		MOV	BP,SP

		PUSH	DS
		PUSH	ES

		MOV     AX,cs:V_MONOSCRNSEG       ;POINT TO THE SCREEN
		MOV	ES,AX

		MOV	CX,[BP + _AOFF + 6]    	;GET THE WIDTH OF MOVE
		CMP	CX,0
		JE	MONO_LINE2
		CMP     CX,cs:V_MONOBYTES
		JL	MONO_LINE1
		MOV     CX,cs:V_MONOBYTES

MONO_LINE1:	MOV	SI,[BP + _AOFF + 0]	;OFFSET OF BITMAP
		MOV	DS,[BP + _AOFF + 2]	;SEGMENT OF BITMAP
		MOV     BX,[BP + _AOFF + 4]     ;NUMBER OF LINE

   mov al,cs:[Row_seg+bx]  ; select the segment
   mov dx,GDC_seg_sel
   out dx,al

		SHL	BX,1

		CLD				;CLEAR DIRECTION FLAG
		MOV     DI,CS:[SCREENTABLE + BX]

   shr cx,1  ; convert to word moves
   jc >l3
   rep movsw
   jmp >l4
l3:
   rep movsw
   movsb
l4:

MONO_LINE2:	POP	ES
		POP	DS

		POP	BP
		RETF


; This routine selects text mode after all the graphics  modes
; are finished.
VGA_off:
EGA_off:
MONO_off:
   mov ax,0003h
   int 10h
   retf

;
;
;this part goes through all available modes to try to find one that is
; larger than the picture or the same size as the picture.
pick_mode:
l3:
   cmp ax,[si]
   ja >l1
   cmp bx,[si+2]
   jbe >l2
l1:
   add si,10
   cmp w[si],0
   jne l3
   sub si,10
l2:
; this part goes backwards through the list of modes to make sure the card
; and moniter can handle the mode.
   mov cx,Installed
   sub dx,dx
   mov dl,ch  ; cx is memory available
   mov ch,dh  ; dx is the moniter
l2:
   cmp cx,[si+4]
   jl >l1
   cmp dx,3
   je >l4  ; any mode moniter
   cmp dx,[si+6]
   je >l4  ; moniter and required are the same
   cmp w[si+6],0
   je >l4  ; VGA moniter is all thats needed
l1:
   sub si,10
   jmp l2
l4:
   mov ax,[si]
   mov bx,[si+2]
   mov cx,[si+8]
   ret
;

   even

;THIS IS A LINE START LOOKUP TABLE

SCREENTABLE:     DW      TLI_DEEP DUP(?)        ;LINE START TABLE
Row_seg: db  TLI_deep dup(?)  ;segments for the lines
   even

;These are the modes supported.  the tables have the format:
;   columns,rows,memory,monitor,mode
; where memory is   0   256k
;                   1   512k
;                   2   1024k
;
; and monitor is  0  standard VGA
;                 1  800x600 capable Multisync
;                 2  1024x768 interlace capable 8514 equivilent
;                 3  1024x768 interlace capable multisync
;
VGA_mode:
   dw  320,200,0,0,13h
   dw  640,400,0,0,2fh   ;  640,350,0,0,2dh for ET3000
   dw  640,480,1,0,2eh
   dw  800,600,1,1,30h
   dw  1024,768,2,2,38h
   dw  0

EGA_mode:
   dw  320,200,0,0,0dh
   dw  640,350,0,0,10h
   dw  640,480,0,0,12h
   dw  800,600,0,1,29h
   dw  1024,768,1,2,37h
   dw  0

MONO_mode:
   dw  640,350,0,0,10h
   dw  640,480,0,0,11h
   dw  800,600,0,1,29h
   dw  1024,768,1,2,37h
   dw  0
