[comp.sys.ibm.pc] Example of a BORLAND BGI device driver

lee@minnow.sp.unisys.com (Gene Lee) (05/10/89)

/* 
The following software can be used and modified freely.  I can not
however be sold in any way.  This includes both seperately and 
compiled into programs.

The following is the work I have done on creating .BGI driver
   files for TURBO-C 2.0 and TURBO PASCAL.  Although the majority
   of the functions work and can be used as is, it is by no means a
   complete .   My main reason for posting this is so that people
   who want to create their own BGI drivers have a good starting point
   and a good example to follow.  The routines that to block fills/reads
   have been written a little trickier than need be. This was to make
   them fast enough to be usable (the tricks are somewhat described
   in the code where they are used).  

   The two main ingredients that went into this were:
	1. A skeleton of a BGI header which was posted to the net
		sometime ago.
	2. The book "Graphics Programming in Turbo-C 2.0" written
	   by Ben Ezzell and published by Addison-Wesley. I found
	   it a B-Dalton.  This book is almost a must if you hope
	   to write a BGI driver.

   I will help answer questions (at least until I get tired of it).
   Mainly I will look gather up all the commo questions and post
   them to the net.

   I also want to make it clear that I am basically a High level
   language programmer and had to learn Assembly as I went to 
   write this.  Therefor, I DONT want to here any comments about
   my poor assembly language programming style.

   REMEMBER, THIS IS MAINLY INTENDED AS A DEVELOPEMENT EXAMPLE
   AND IS NOT INTENDED TO BE A RELEASED PRODUCT ( although almost
   all of the functions do work for this graphics card ).

======================= CUT APART HERE AND NAME BGITEST.C ===========
/*

	This is a real hack of a demo program but it at least shows you
	what your C program must do to install and use a user-written
	device driver.  I had most of the BORLAND SUPPLIED bgidemo.c
	program working after I took out their palette demo routine
	and change the program to use another font other than DEFAULT
	( which I could not make work ).  I didn't post I because I 
	was afraid BORLAND could bust me for posting it.
	
*/



#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>

int huge detectLEEVGA(void);


main()
{

	int driver, mode;
	int ysize, xsize;
	int i;


driver =installuserdriver ("LEEVGA", detectLEEVGA);


	{
	int ec;
	if ((ec=graphresult()) < 0)  {
		printf("unable to register\n");
		printf("reason <%s>\n", grapherrormsg(ec));
		exit(-1);
		return(-1);
	}
	}


 driver = DETECT;
	initgraph(&driver, &mode, "");

	{
	int ec;
	if ((ec=graphresult()) < 0)  {
		printf("unable to open this graphics mode\n");
		printf("reason <%s>\n", grapherrormsg(ec));
		exit(-1);
		return(-1);
	}
	}


printf("  max x=       %d\n",	xsize=getmaxx());
printf("  max y=       %d\n",	ysize=getmaxy());
printf("  modename=    %s\n", getmodename(0));
printf("  drivername=  %s\n", getdrivername());
printf("  maxcolors=   %d\n", getmaxcolor());
printf("  palette size=%d\n", getpalettesize());


#define DO 1
#ifdef DO
	setfillstyle ( SOLID_FILL, 15 );
	setcolor ( 12 );

	outtextxy(xsize/6,ysize/3,"A b C d E f G h I J k"); getch();
	printf("  Outtext  ( DEFAULT FONT )  is done\n"); getch();

	settextstyle(TRIPLEX_FONT,HORIZ_DIR,4);
	outtextxy(xsize/6,ysize/4,"0 1 2 3 4 5 6 7 8 9 0");
	printf("  Outtext  ( TRIPLEX_FONT )  is done\n"); getch();

setlinestyle(DASHED_LINE,0,NORM_WIDTH);

 for (i=10; i < xsize/3*2 ; i++) {
	putpixel(i,ysize/4,12);
}
printf("  pixel puts are done, press a key\n");getch();

#endif

setfillstyle ( SLASH_FILL, 11 );
setlinestyle(DASHED_LINE,0,NORM_WIDTH);

pieslice(xsize/2,ysize/2,45,45+90,ysize/3);


circle (xsize/2, ysize/2,ysize/2);
ellipse(xsize/2,ysize/2,0,0,100,100);
printf("  circle done press a key\n");getch();

setlinestyle(CENTER_LINE,0,NORM_WIDTH);
line (  10,ysize/2,ysize-100,ysize/2);
line ( xsize/2, 10 , xsize/2 , ysize/2 );
printf("  lines done, press a key\n");getch();

rectangle (10,20,xsize/6,ysize/2);
printf("  rectangle done, press a key\n");getch();


setlinestyle(DOTTED_LINE,0,THICK_WIDTH);

setfillstyle ( XHATCH_FILL, 21 );
setcolor(83);
bar3d ( xsize/2,ysize/2,  xsize/6*5,ysize, 50, 1);
printf("  bar is done, press a key\n");getch();
setlinestyle(DASHED_LINE,0,NORM_WIDTH);


{
	int pp[10]={100,100, 100,150, 180,195, 300,150, 330,100};

setfillstyle ( CLOSE_DOT_FILL, 11 );

fillpoly ( 5 , &pp );
printf("  filled polygon is done, press a key\n");getch();
}




setrgbpalette(12,1,2,3);

{
typedef struct {
	char r,g,b;
} entry;

struct p {
	char size;
	entry index[256];
} palette;

palette.size = 3*256;
palette.index[0].r =1;
palette.index[0].g =2;
palette.index[0].b =3;


setallpalette(&palette);
}




 cleardevice();
 printf("  clear done\n");


 printf("  press a key\n");getch();
 closegraph();

}




/* ______________________ */

int huge detectLEEVGA(void)
{

	printf("detecting\n");
	
	/*
		You must write code here to detect whether or not
		the graphics card is available and what its maximum mode
		is.  By returning the value of ONE here, I am telling 
		it I have detected my graphics card and the mode I want 
		it to use is ONE.
		
	*/	

	return(1);
}

======================== CUT APART HERE and NAME IT LEEVGA.ASM ==========

; know problems
;	1. the default font has not been done.
;	2. flood fill has not been done.

; to compile (create) the .BGI file use the following

; masm leevga.asm leevga.obj NUL.LST NUL.LST
; link leevga.obj,leevga.exe,NUL.MAP,.LIB
; exe2bin leevga.exe leevga.bgi
; del leevga.exe
; del leevga.obj


;------------------------------------------------------;
;               LEEBGI.BGI                             ;
;     Borland Graphic Interface for TP 5.0, TC2.0      ;
;       Paridise VGA Graphics Board                    ;
;         Copyright (c) 1989 Gene W. Lee               ;
;------------------------------------------------------;
;
ACODE       segment  'BGI'
;
;
astart      DB      'pk'  ; BGI magic word (Phillipe K.)
            DB      8,8
			DB      'BGI Device Driver V1.0 Gene W. Lee - 1989 '
            db      0Dh,0Ah
			DB      'Copyright (c) 1989 Gene W. Lee',0Dh,0Ah
            db      0,1Ah
            dw      00A0h,11
            dw      endofcode  ; This is a label that precedes CODE ENDS
            dw      2
            db      1,1
            org     80h
            dw      00A0h,11
            dw      endofcode  ; see above
            dw      2
			db      1,1

;    The following is returned by GetDriverName.
			db      6,'LEEVGA'
alen        =       $-astart              ; Pad this segment out to
            db      (160 - alen) dup(0)   ; 160 bytes
ACODE       ends

CODE        SEGMENT 'BGI'
;
; Now for the real BGI driver code. A separate segment is required
; because although this code starts at offset 0A0h in the file
; ATTDEB.BGI, GRAPH will adjust its address up by 10 paras and then
; do a CALL FAR xxxx:[0000]. So all memrefs have to be 0 relative to
; the label BGIENTRY below.  IMPORTANT: All code from here to JMP_TABLE
; needs to be exactly as it is.  GRAPH expects the label EMULATE to be
; at the offset that it is in this code (GRAPH is going to shove code
;
			ASSUME  CS:CODE,DS:CODE,ES:CODE
BGIENTRY    PROC    FAR        ; "FAR" So that the RET is a RETF
			PUSH    DS
            PUSH    CS
			POP     DS
            CLD
			PUSH    BP
;
; The Function number to be performed in SI.  SI is used to index into
; a jump table.  Note: all SI values are even.
;
			CALL    [SI+JMP_TABLE]  ; See JMP_TABLE
			POP     BP
			POP     DS
			RET
BGIENTRY    ENDP
;
			db      'CB'        ; ?
			dw      0
;
EMULATE      PROC NEAR          ; This is the entry point of functions
			RET                 ; you want Turbo XX to emulate.  Note:
			dw      0           ; only select entries may be emulated.
			dw      0
EMULATE      ENDP

NOP_02      PROC NEAR           ; This is the entry point for all functions
			RET                 ; which are not implemented.
NOP_02      ENDP

; Now for the Jump Table; The SVC_xx labels correspond to the
; value in SI when the driver is called(ie: SVC_00 is the entry point
; when SI = 0.  IF the labels are EMULATE or NOP_02 the value of SI is
; to the right in braces.  NOP_02 means this function is a NOP and
; GRAPH should never call it (at least relative to TP5).  The label
; EMULATE is a JMP to a CALL back to GRAPH to perform the function.
; (Aren't you glad you don't have to write a Circle Drawing Algorithm!)
;
; It should also be noted that calls to the BGI code are both pre-
; processed  and post-processed by GRAPH.  Some calls never make it to
; your code. One example is the DisplayText functions.  The only time
; that calls make it here is for the bit-map text displays.  GRAPH
; handles all of the stroked CHR fonts.
;
jmp_table	dw install  		; {0} Initialize
			dw init  			; {2} Set Mode
			dw clear			; {4} Clear Graphics Screen
			dw nop_02			; {06} POST
			dw move				; {8} current pointer (CP)
			dw draw				; {A} draw line from CP to new CP
			dw vect				; {C} Draw Line
			dw EMULATE			; {0E} Draw/Fill Poly
			dw emulate			; {10} Bar3D
			dw patbar 	  		; {12} pattern Bar
			dw EMULATE			; {14} Draw Circle
			dw EMULATE			; {16} Draw Pie Slice
			dw EMULATE			; {18} Draw Ellipse
			dw palette          ; {1A} Set Palette
			dw SVC_1C  ; {1C} Set All Palette
			dw color			; {1E} Set Color
			dw fillstyle		; {20} Set Fill Style/Pattern
			dw linestyle		; {22} Set Line Style/Pattern
			dw textstyle  		; {24} Set UserCharSize
			dw text				; {26} Display Text
			dw textsize			; {28} Text Width/Height
			dw EMULATE			; {2A}   ; I never figured this one out !
			dw flood_fill		; {2C} FloodFill
			dw get_pixel		; {2E} GetPixel
			dw put_pixel		; {30} SetPixel
			dw bitmaputil		; {32} Set CallTable
			dw get_image	    ; {34} GetImage
			dw put_image        ; {36} PutImage
			dw set_clip			; {38} Set View Min/Max
			dw color_query		; {3A} SetParameters
			dw emulate          ; {3C} reserved
			dw EMULATE          ; {3E} symbol

			dw NOP_03           ; 40
			dw NOP_03           ; 42
			dw NOP_03           ; 44
			dw NOP_03           ; 46
			dw NOP_03           ; 48
			dw NOP_03           ; 4A
			dw NOP_03           ; 4C
			dw NOP_03           ; 4E
			dw NOP_03           ; 50
			dw NOP_03           ; 52
			dw NOP_03           ; 54
			dw NOP_03           ; 56
			dw NOP_03           ; 46
			dw NOP_03           ; 48
			dw NOP_03           ; 4A
			dw NOP_03           ; 4C
			dw NOP_03           ; 4E
			dw NOP_03           ; 50
			dw NOP_03           ; 52
			dw NOP_03           ; 54
			dw NOP_03           ; 56
			dw NOP_03           ; 46
			dw NOP_03           ; 48
			dw NOP_03           ; 4A
			dw NOP_03           ; 4C
			dw NOP_03           ; 4E
			dw NOP_03           ; 50
			dw NOP_03           ; 46
			dw NOP_03           ; 48
			dw NOP_03           ; 52
			dw NOP_03           ; 54
			dw NOP_03           ; 56
;

NOP_03      PROC FAR                 ; stub procedure for far returns
			RET
NOP_03      ENDP


;

;                     structure for 320x200 256 color mode
statustype0	struc
		db	0     	;  current device status
		db	0		;  device type identifier ( must be zero )
		dw	319		;  full resolution in the x direction
		dw	199		;  full resolution in the y direction
xefres0 dw	319		;  effective resolution in x direction
		dw	199		;  effective resolution in y direction
		dw	9000	;  device x size in inches * 1000
		dw	7000	;  device y size in inches * 1000
		dw	8572	;  aspect ratio = ( ysize / xsize ) * 1000
					;  next four bytes are for compatibilty
		db	8h
		db	8h
		db	90h
		db	90h
statustype0	ends

status0	statustype0<>

;					structure for 640x400 256 color mode (0x5E)
statustype1	struc
		db	0     	;  current device status
		db	0		;  device type identifier ( must be zero )
		dw	639		;  full resolution in the x direction
		dw	399		;  full resolution in the y direction
xefres1	dw	639		;  effective resolution in x direction
		dw	399		;  effective resolution in y direction
		dw	9000	;  device x size in inches * 1000
		dw	7000	;  device y size in inches * 1000
		dw	8572	;  aspect ratio = ( ysize / xsize ) * 1000
					;  next four bytes are for compatibilty
		db	8h
		db	8h
		db	90h
		db	90h
statustype1	ends

status1	statustype1<>

active_mode db	?     ;  the currently active mode number

modestring0	db  18,'320 x 200 VGA Mode',0
modestring1	db  18,'640 x 400 VGA Mode',0

xsize	dw	?         ; the xsize of the currently selected mode
max_x	dw	?		  ; the maximum value of x for the current mode



;		table for ellipse function calls
;  Let it used my put_pixel routine, there is not much I could do to speed
;  up pixel writing anyway.
the_util_table	dw offset NOP_03		; enter pixel mode
				dw offset NOP_03		; Leave pixel mode
				dw offset NOP_03   		; write a pixel
				dw offset NOP_03    	; get a pixel
				dw offset bitsperpixel		; return bits per pixel in AX
				dw offset NOP_03		; set draw page
				dw offset NOP_03		; set visual page
				dw offset NOP_03		; set xor mode

enter_pixel_mode proc far
	call unlock_vga
	mov doing_ellipse,1
	ret
enter_pixel_mode endp

exit_pixel_mode proc far
	call lock_vga
	mov doing_ellipse,0
	ret
exit_pixel_mode endp

bitsperpixel proc far
	mov ax,8              ; eight bits per pixel in VGA mode
	ret
bitsperpixel endp



								; the bit patterns for default linestyles
default_linestyles	dw	0FFFFh  ; SOLID_LINE
					dw	0CCCCh	; DOTTED_LINE
					dw	0FC78h	; CENTER_LINE
					dw	0F8F8h	; DASHED_LINE

active_linestyle	dw	0FFFFh	; the currently selected line style is written
								; into this word.
line_bit_ptr		db	0		; pointer into linestyle word (bit ptr)


default_fillpaterns db	000h,000h,000h,000h,000h,000h,000h,000h ; No Fill
	db  0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh ; Solid fill
	db  0FFh,0FFh,000h,000h,0FFh,0FFh,000h,000h ; Line FIll
	db  001h,002h,004h,008h,010h,020h,040h,080h ; Lt Slash fil
	db  0E0h,0C1h,083h,007h,00Eh,01Ch,038h,070h ; Slash fill
	db  0F0h,078h,03Ch,01Eh,00Fh,087h,0C3h,0E1h ; Backslash
	db  0A5h,0D2h,069h,0B4h,05Ah,02Dh,096h,04Bh ; lt Backslash
	db  0FFh,088h,088h,088h,0FFh,088h,088h,088h ; Hatch Fill
	db  081h,042h,024h,018h,018h,024h,042h,081h ; XHatch Fill
	db  0CCh,033h,0CCh,033h,0CCh,033h,0CCh,033h ; interleave
	db  080h,000h,008h,000h,080h,000h,008h,000h ; Wide Dot
	db  088h,000h,022h,000h,088h,000h,022h,000h ; Close Dot

active_fillpattern	db	?,?,?,?,?,?,?,? 	; the currently selected fill
										  ; style is written into this array.
fill_byte_ptr		dw  ?       ; pointer to fill pattern byte
fill_bit_ptr		dw  ?		; pointer to bit of fill pattern byte




;  I don't really understand what this is all about. I just tell it I don't
;  have a default color table
color_table db	0

current_background_color  db ?
current_line_color	db	?	; the currently selected color for lines
current_fill_color	db  ?   ; the currently selected fill color
cp_x				dw	?   ; the current pointer - x
cp_y				dw	?   ; the current pointer - y
clip_x1				dw	?   ; clipping window
clip_y1				dw	?   ; clipping window
clip_x2				dw	?   ; clipping window
clip_y2				dw	?   ; clipping window


; start x,y and end x,y for line draws
x1	dw	?
y1	dw	?
x2	dw	?
y2	dw	?

; vars used by line draw routine
delta_x	dw	?
delta_y	dw	?
count	dw	?
halfx	label word          ;   only need halfx or halfy at one time
halfy	dw	?

; vars used to do pixel read/writes in VGA mode
lasthio dw -1         ; keeps track of the last hioffset value calculated
next_row    dw  ?     ; variable used by patbar, getimage and putimage

; vars used by palette routines
red   db ?
green db ?
blue  db ?

doing_ellipse db 0		; set true when unlock has been called to start
			; drawing an ellipse. Saves time on ellipse draws



; 0 ----------------------------------------------------------------------

; if al=0       ( install device )
; 	input:  cl = mode number for device
; 		ch = autodetect maximum device number
; 	return: es:bx = pointer to device status table
; if al=1       ( mode query )
; 	input: nothing
; 	return: cx = number of modes supported by this device
; if al=2       ( return mode name )
; 	input cx: the mode number for query
; 	return es:bx = pointer to PASCAL string containing the name

install:
		cmp al,0
		jz 	installit
		cmp	al,1
		jz	modequery

		; else it must be a modename() call
		push cs
		pop es
		cmp cx,0
		je  modename0
		mov bx, offset modestring1         ; return pointer to mode name
		ret

modename0:
		mov bx, offset modestring0         ; return pointer to mode name
		ret

installit:
		mov active_mode,cl      ; remember what mode were in
		cmp cl,1
		je install_mode1
						   ; else we install mode 0
               ; keep track of current x and y screen size
		mov ax,status0.xefres0 
		mov max_x,ax
		inc ax
		mov xsize,ax

		push cs
		pop es
		mov bx,offset status0   ; return location of status table
		ret

install_mode1:
		mov ax,status1.xefres1      ; keep track of current x and y screen size
		mov max_x,ax
		inc ax
		mov xsize,ax

		push cs
		pop es
		mov bx,offset status1   ; return location of status table
		ret

modequery:
		mov cx,2		; only two modes supported for now
		ret

; 2 ----------------------------------------------------------------------

; input:
; 	es:bx = pointer to device table
; return: nothing

init:
		cmp active_mode,1
		je init_mode1
						; else we init mode 0
		mov ax,13h
		int 10h

		ret


init_mode1:
		mov ax,5Eh
		int 10h

		ret


; 4 ----------------------------------------------------------------------

; input: nothing
; output: nothing
;            clear the device

clear:
		call init
		ret


; 8 ------------------------------------------------------------------------

; input:
;	ax = the new CP x coord
;	bx = the new CP y coord
; return: nothing
move:           ; set current pointer (CP)

				; load the new current pointer
		mov cp_x,ax
		mov cp_y,bx
		ret



; A -----------------------------------------------------------------------

;        draw line from CP to x,y
; input:
;	ax: ending x coord
;	bx: ending y coord
; output:
;	nothing

draw: 					; draw line from CP to new CP

		push cx
		mov cx,cp_x         ; draw starts at current position (cp)
		mov x1,cx
		mov	cx,cp_y
		mov	y1,cx
		pop cx

		mov x2,ax           ; load the end points
		mov y2,bx

		mov	cp_x,ax			; end points are now new current points
		mov	cp_y,bx

		mov line_bit_ptr,0  ; reset the line style

		call draw_line		; draw the line from x1,y1 to x2,y2

		ret


; C ----------------------------------------------------------------------

; draw line from x1,y1 to x2,y2

; input:
;	ax = x1
;	bx = y1
;	cx = x2
;	dx = y1
; output:
;	nothing

vect:						; Draw Line

							; setup the start and end points
		mov	x1,ax
		mov	y1,bx
		mov	x2,cx
		mov	y2,dx

		mov line_bit_ptr,0  ; reset the line style

		call draw_line		; draw a line

		ret





; 1A --------------------------------------------------------------------

; set a single palette entry
; input:
;	ax = the index number to load
;	bx = the color value
; return: nothing

palette:

		push ax
		push cx

		mov cl,14
		shr ax,cl         ; only want upper to bits of cl

		cmp ax,010b        ; set rgb palette

		pop cx
		pop ax

		jne p_done        ; the other values are meaningless in vga mode

		mov red,bl
		mov green,cl
		mov blue,dl
		    ; get rid of upper two control bits, leave index
		and ax,03FFFh  


		mov dh,red
		mov ch,green
		mov cl,blue
		mov bx,ax

		mov ax,01010h     ; set single palette entry

		int 10h


p_done:
		ret



; 1C ---------------------------------------------------------------------

	 ; Set all Palette entries with one call
SVC_1C: ; Set All Palette
	;  not compatible with 256 color VGA modes, you have to provide
	; the user with a library function to do this.
		ret


; 1E -------------------------------------------------------------------

; input: 
;	al = index number of current drawing color
;	ah = index number of current fill color
; return: nothing

color: ; Set Color

		mov	current_line_color,al
		mov current_fill_color,ah

		ret


; 20 ------------------------------------------------------------------

; input:
;	al = fill pattern number
;	es:bx = if pattern number = 0FFh, this points to user fill pattern
; return: nothing

fillstyle:				; Set Fill Style/Pattern


		cmp al,0FFh     ; test if user defined fill
		jz	user_fill

		cbw
		mov cl,3		; multiply by 8 for eight bytes per fill type
		shl ax,cl
		mov si,ax
		add si,offset default_fillpaterns ; point to the selected fill patern
		jmp cont_fill   ; ds:si point to selected fill pattern

user_fill:              ; user defined fill passed by pointer ES:BX
		push bx
		pop  si
		push es
		pop  ds         ; setup so ds:si points to users fill pattern

cont_fill:

		mov	di,offset active_fillpattern
		push cs
		pop	es			; es:cs is the destination of the copy

		mov	cx,8		; setup to copy eight bytes of pattern
		repz movsb      ; copy pattern into active pattern

		ret


; 22 ------------------------------------------------------------------

; input:
;	al = line pattern number
;	bx = user defined line pattern if al = 4
;	cx = line width
; return: nothing

linestyle:      ;          set current line style

		cmp	ax,4
		jz	user_defined		; check if its a user defined line

		and	ax,0FFh				; pattern number in AL
		shl	al,1				; used two index by 2 bytes at a time
		mov	si,ax
		mov	bx,[default_linestyles +si]  ; load a pre-defined pattern

user_defined:               	; user defined are passed in BX
		mov active_linestyle,bx

		ret



; 24 -------------------------------------------------------------------

; input:
;	al = hardware font number
;	ah = hardware font orientation
;	bx = desired X size
;	cx = desired Y size
; return:
;	bx = closest X size available
;	cx = closest Y size available

textstyle:				; Set UserCharSize
		; this code works but thats about all
		mov bx,bx       ; let say the characters are 8 x 16
		mov cx,cx
		ret



; 26 --------------------------------------------------------------------

; input:
;	es:bx = pointer to ascii string to display
;	cx = number of chars in string
;	al = horizontal justification ( 0=left,1=center,2=right )
;       ah = vertical justification (0=bottom,1=center,2=top)
; return:
;	bx = width of string in graphics units
;	cx = height of string in graphics units

text:				 ; Display Text  ( DEFAULT FONT )

	; this is one of the things I could not figure out
	; if you come up with something please let me know.
		push bx
		push cx
		; hack here


		; end hack
		pop cx
		pop bx
		mov bx,bx    ;  NOTE this should be 8 * length of string (cx)
		mov cx,cx
		ret


; 28 --------------------------------------------------------------------

; input:
;	es:bx = pointer to ascii text
;	cx = number of chars in test
; return:
;	bx = width of string in graphics units
;	cx = height of string in graphics units

textsize:			 ; Text Width/Height ( DEFAULT FONT )

;	again I did not know how to do this
		mov bx,bx    ; NOTE this is not right
		mov cx,cx
		ret


; 2C --------------------------------------------------------------------

; input:
;	ax = x coord to start at
;	bx = y coord to start at
;	cl = boundry color
; return: nothing

flood_fill:			 ; FloodFill
		; flood fill needs implemented also
		ret


; 2E -----------------------------------------------------------------

; input:
;	ax = x coord
;	bx = y coord
; return:
;	dl = color 

get_pixel:	 		; GetPixel

		; if you don't mind being slow, just use the bios calls
;		mov	cx,ax      ; load x value
;		mov	dx,bx		; load y value
;		mov bh,0		; page zero
;		mov ah,0Dh      ; read pixel function
;
;		int 10h
;
;		mov dl,al       ; return the color of pixel read
;		ret


		cmp active_mode,1
		je getpixel_mode1

					   ; else read pixels in mode 0
		push di        ; save these registers
		push es

		mov  cx,ax	   ;   save the column value (x)


		mov ax,bx      ;   load the row value (y)
		mul xsize      ;   offset =(xsize * y)
		add ax,cx	   ;     + x
		mov di,ax      ;   load screen address into di

		mov ax,0A000h  ;   video ram segement address
		mov es,ax
		mov dl,es:[di] ;   read the pixel


		pop es
		pop di

		ret



getpixel_mode1:        ; read pixel this way when in mode 1
		push di        ; save these registers
		push es

		mov  cx,ax	   ;   save the column value (x)

		mov ax,bx      ;   load the row value (y)

		call calc_hilow   ; find vga offset values into screen memory

		cmp bx,lasthio ;   see of hioffset is the same as last time
		je  no_hio_chg_r ;   if it is, save time by not resetting it

		cmp doing_ellipse,1
		je no_unlock_get      ; its already unlocked
		call unlock_vga
no_unlock_get:

		mov ax,bx      ;
		call set_hioffset  ; sets the hioffset register for vga mem addressing

		cmp doing_ellipse,1
		je no_lock_get
		call lock_vga
no_lock_get:

no_hio_chg_r:
		mov ax,0A000h  ;   video ram segement address
		mov es,ax
		mov dl,es:[di] ;   read the pixel


		pop es
		pop di

		ret



; 30 -----------------------------------------------------------------

; input:
;	ax = x coord
;	bx = y coord
;	dl = color 
; return: nothing


put_pixel:			; SetPixel

; 		p[XSIZE*y+x] = color;           for 320 x 200 - 256 mode

		; if you don't mind being slow, just call the bios plot dot
;		push dx        ; save color value
;		mov	cx,ax      ; load x value
;		mov	dx,bx		; load y value
;		pop ax          ; color value from pushed dx
;		mov bh,0		; page zero
;		mov ah,0Ch      ; write pixel function
;
;		int 10h
;		ret

; else get fancy to make it faster.

		cmp active_mode,1
		je putpixel_mode1

					   ; else plot pixels in mode 0
		push di        ; save these registers
		push es
		push ax
		push bx
		push cx
		push dx

		push dx        ;   save the color
		mov  cx,ax	   ;   save the column value (x)

		mov ax,bx      ;   load the row value (y)
		mul xsize      ;   offset =(xsize * y)
		add ax,cx	   ;     + x
		mov di,ax      ;   load screen address into di

		mov ax,0A000h  ;   video ram segement address
		mov es,ax

		pop dx         ;   get the color info back

		mov es:[di],dl ;   plot the pixel

		pop dx
		pop cx
		pop bx
		pop ax
		pop es
		pop di

		ret




putpixel_mode1:                  ; routine to plot pixels when in mode 1
		push di        ; save these registers
		push es
		push ax
		push bx
		push cx
		push dx

		push dx        ;   save the color
		mov  cx,ax	   ;   save the column value (x)

		mov ax,bx      ;   load the row value (y)

		call calc_hilow     ;   find vga hi and low offset value for screen mem

		cmp bx,lasthio ;   see of hioffset is the same as last time
		je  no_hio_chg_w ;   if it is, save time by not resetting it

		cmp doing_ellipse,1
		je no_unlock_put     ; its already done
		call unlock_vga
no_unlock_put:

		mov ax,bx      ;
		call set_hioffset  ; sets the hioffset register for vga mem addressing

		cmp doing_ellipse,1
		je no_lock_put
		call lock_vga
no_lock_put:

no_hio_chg_w:
		mov ax,0A000h  ;   video ram segement address
		mov es,ax

		pop dx         ;   get the color info back

		mov es:[di],dl ;   plot the pixel

		pop dx
		pop cx
		pop bx
		pop ax
		pop es
		pop di

		ret




; 32 -------------------------------------------------------------------

bitmaputil: ; Set CallTable

			; see the book, basically don't worry (be happy)
		
		push cs
		pop es
		mov bx, offset the_util_table
		ret

; 34 -------------------------------------------------------------------

; input:
;	ax = x1 corner
;	bx = y1 corner
;	cx = x2 corner
;	dx = y2 corner
; return: nothing

patbar:           ; fill the rectangle using current fillstyle and color

;        this version of pattern filling uses the following trick to greatly
;        improve speed.  Instead of calling put_pixel for every pixel to plot
;        it does the write to screen memory on its own. What makes it fast is
;        that it only has to find the original offset by multipling XSIZE by
;        y1.  From then on it increments a pointer to find the next location
;        in screen memory (not multiplies required after initial one).

					; first, make sure x1 < x2 and y1 < y2
		cmp ax,cx               ; if x1 < x2
		jle x_ok
		xchg ax,cx
x_ok:

		cmp bx,dx              ; if y1 < y2
		jle y_ok
		xchg bx,dx
y_ok:

					; then load the start and end values
		mov x1,ax
		mov x2,cx
		mov y1,bx
		mov y2,dx


		cmp active_mode,1
		je patbar_mode1			; different way to calc pixel offset

		;         Next, setup the starting value of the VGA's hioffset reg
		push ax
		push bx
		push cx

		mov cx,ax           ; load x value
		mov ax,bx           ; load the y value
		mul xsize        ; find the pixel offset
		add ax,cx        ; add inthe x value
		mov di,ax

		mov ax,0a000h       ; screen memory segment
		mov es,ax

		;  calculate ho many screen byte are from right edge of box
		;  to left edge of box on next row
		mov ax,max_x
		sub ax,x2
		add ax,x1
		mov next_row,ax

		pop cx
		pop bx
		pop ax

again0:

		;   First, lets figure out the fill pattern parameters


		push ax
		push cx
		push si



		; the bit/byte pointer are relative to the absolute
		; screen position mod 8
		mov fill_byte_ptr,bx    ; load the current y value
		mov fill_bit_ptr,ax     ; load the current x value
		and fill_byte_ptr,07h
		and fill_bit_ptr,07h


		mov si,fill_byte_ptr;
		mov al,[offset active_fillpattern + si]
		mov cx,fill_bit_ptr
		shl al,cl
		and al,80h
		cmp al,80h


		jne use_background0              ; if not plotting this dot
		mov dl,current_fill_color      ; else load fill color
		jmp selected0

use_background0:
		mov dl,current_background_color  ; assume background

selected0:

		pop si
		pop cx
		pop ax




; 		this is the slow way, it has to recalute the offset into
;               display memory every time it puts a pixel

;		push ax
;		push bx
;		call put_pixel
;		pop bx
;		pop ax

		mov es:[di],dl


		inc di              ; advvance screen mapped memory pointer
		inc ax              ; advance in x dir till at right edge
		cmp ax,x2
		jle again0

		mov ax,x1                ; reset to left size of rectangle
		inc bx                   ; go to down to next row
		cmp bx,y2
		jg  fill_done0


		;			Since were changing rows we'll find a new hi and low
		; the trick is to add the number of bytes from the right side of the
		; box to the left side of the box  ( note: no multiplies needed )

		add di,next_row

		jmp again0



fill_done0:
		ret              ; end of patbar in mode 0




patbar_mode1:
			; first, make sure x1 < x2 and y1 < y2

		call unlock_vga  ;  keep vga ready to plot pixels


		;   Next, setup the starting value of the VGA's hioffset reg
		push ax
		push bx
		push cx

		mov cx,ax           ; load x value
		mov ax,bx           ; load the y value
		call calc_hilow     ; find the values of hio and lowo
		mov ax,bx           ; load hio
		call set_hioffset   ; set the vga register for the hioffset

		mov ax,0a000h       ; screen memory segment
		mov es,ax

		;  calculate ho many screen byte are from right edge of box
		;  to left edge of box on next row
		mov ax,max_x
		sub ax,x2
		add ax,x1
		mov next_row,ax

		pop cx
		pop bx
		pop ax

again1:

		;   First, lets figure out the fill pattern parameters


		push ax
		push cx
		push si



		; the bit/byte pointer are relative to the absolute
		; screen position mod 8
		mov fill_byte_ptr,bx    ; load the current y value
		mov fill_bit_ptr,ax     ; load the current x value
		and fill_byte_ptr,07h
		and fill_bit_ptr,07h


		mov si,fill_byte_ptr;
		mov al,[offset active_fillpattern + si]
		mov cx,fill_bit_ptr
		shl al,cl
		and al,80h
		cmp al,80h


		jne use_background1              ; if not plotting this dot
		mov dl,current_fill_color      ; else load fill color
		jmp selected1

use_background1:
		mov dl,current_background_color  ; assume background

selected1:


		pop si
		pop cx
		pop ax




;     	this is the slow way, it has to recalute the offset into
;          display memory every time it puts a pixel
;        but it shows the easy way to do it.

;		push ax
;		push bx
;		call put_pixel
;		pop bx
;		pop ax

		mov es:[di],dl


		inc di
		cmp di,0FFFh
		jle leave_hio1
		sub di,1000h
		inc lasthio

		push ax            ; let vga card know we incremented hi offset
		mov ax,lasthio
		call set_hioffset
		pop ax

leave_hio1:
		inc ax              ; advance in x dir till at right edge
		cmp ax,x2
		jle again1

		mov ax,x1                ; reset to left size of rectangle
		inc bx                   ; go to down to next row
		cmp bx,y2
		jg  fill_done1


	;Since were changing rows we'll find a new hi and low
	; the trick is to add the number of bytes from the right side of the
	; box to the left side of the box  ( note: no multiplies needed )

		add di,next_row
		cmp di,0FFFh
		jle no_bump1
		sub di,1000h
		inc lasthio
		push ax
		mov ax,lasthio
		call set_hioffset
		pop ax
no_bump1:


		jmp again1



fill_done1:
		call lock_vga           ; protect the vgas register again

		ret



; 36 ------------------------------------------------------------------

buf_ptr   dw    ?        ; pointer to the buffer for getimage/putimage
put_mode  db    ?	 ; pixel writing mode for putting images (and,xor,etc)

;         NOTE: the book has mistakes in input/output parameters
; input:
; 	es:bx = pointer to memory buffer
;       buffer[0] = window width in x direction
;       buffer[2] = window height in y direction
;	cx = x1 corner of window
;	dx = y1 corner of window
; return: nothing

get_image: ; GetImage


;       this version of pattern filling uses the following trick to greatly
;       improve speed.  Instead of calling put_pixel for every pixel to plot
;       it does the write to screen memory on its own. What makes it fast is
;       that it only has to find the original offset by multipling XSIZE by
;       y1.  From then on it increments a pointer to find the next location
;       in screen memory.


		mov buf_ptr,bx   ; pointer to the buffer where we'll store image

		mov ax,es:[bx]
		add ax,cx   ; add x1 plus xsize of image window

		mov bx,es:[bx +2]
		add bx,dx   ; add y1 plus ysize of image window


		mov x1,cx
		mov y1,dx
		mov x2,ax
		mov y2,bx

		mov bx,buf_ptr
		add bx,4           ; first word was used for xsize dimension
		mov buf_ptr,bx

		mov ax,x1
		mov bx,y1



		cmp active_mode,1
		je getimage_mode1	; different way to calc pixel offset

		; Next, setup the starting value of the VGA's hioffset reg
		push ax
		push bx
		push cx

		mov cx,ax           ; load x value
		mov ax,bx           ; load the y value
		mul xsize        ; find the pixel offset
		add ax,cx        ; add inthe x value
		mov di,ax


		;  calculate ho many screen byte are from right edge of box
		;  to left edge of box on next row
		mov ax,max_x
		sub ax,x2
		add ax,x1
		mov next_row,ax

		pop cx
		pop bx
		pop ax

g_again0:
		; move a byte from the screen to the image buffer
		push ax
		push es
		mov ax,0A000h
		mov es,ax
		mov al,byte ptr es:[di]
		pop es
		push bx
		mov bx,buf_ptr
		mov byte ptr es:[bx],al
		pop bx
		pop ax
		inc buf_ptr  ; point to next byte in the users buffer


		inc di              ; advvance screen mapped memory pointer
		inc ax              ; advance in x dir till at right edge
		cmp ax,x2
		jle g_again0

		mov ax,x1                ; reset to left size of rectangle
		inc bx                   ; go to down to next row
		cmp bx,y2
		jg  geti_done0


	;Since were changing rows we'll find a new hi and low
	;the trick is to add the number of bytes from the right side of the
	; box to the left side of the box  ( note: no multiplies needed )

		add di,next_row

		jmp g_again0



geti_done0:
		ret              ; end of patbar in mode 0




getimage_mode1:
					; first, make sure x1 < x2 and y1 < y2

		call unlock_vga  ;  keep vga ready to plot pixels


		;  Next, setup the starting value of the VGA's hioffset reg
		push ax
		push bx
		push cx

		mov cx,ax           ; load x value
		mov ax,bx           ; load the y value
		call calc_hilow     ; find the values of hio and lowo
		mov ax,bx           ; load hio
		call set_hioffset   ; set the vga register for the hioffset

		;  calculate ho many screen byte are from right edge of box
		;  to left edge of box on next row
		mov ax,max_x
		sub ax,x2
		add ax,x1
		mov next_row,ax

		pop cx
		pop bx
		pop ax

g_again1:

			; move a byte from the screen to the image buffer
		push ax
		push es
		mov ax,0A000h
		mov es,ax
		mov al,byte ptr es:[di]
		pop es
		push bx
		mov bx,buf_ptr
		mov byte ptr es:[bx],al
		pop bx
		pop ax
		inc buf_ptr  ; point to next byte in the users buffer


		inc di
		cmp di,0FFFh
		jle g_leave_hio1
		sub di,1000h
		inc lasthio

		push ax          ; let vga card know we incremented hi offset
		mov ax,lasthio
		call set_hioffset
		pop ax

g_leave_hio1:
		inc ax              ; advance in x dir till at right edge
		cmp ax,x2
		jle g_again1

		mov ax,x1                ; reset to left size of rectangle
		inc bx                   ; go to down to next row
		cmp bx,y2
		jg  geti_done1


	;Since were changing rows we'll find a new hi and low
	;the trick is to add the number of bytes from the right side of the
	; box to the left side of the box  ( note: no multiplies needed )

		add di,next_row
		cmp di,0FFFh
		jle g_no_bump1
		sub di,1000h
		inc lasthio
		push ax
		mov ax,lasthio
		call set_hioffset
		pop ax
g_no_bump1:

		jmp g_again1

geti_done1:
		call lock_vga           ; protect the vgas register again

		ret

; 38 -------------------------------------------------------------------------

;         NOTE: the book has mistakes in input/output parameters
; input:
; 	es:bx = pointer to memory buffer
;       buffer[0] = window width in x direction
;       buffer[2] = window height in y direction
;	cx = x1 corner of window
;	dx = y1 corner of window
; return: nothing


put_image: ; putImage

;        this version of pattern filling uses the following trick to greatly
;        improve speed.  Instead of calling put_pixel for every pixel to plot
;        it does the write to screen memory on its own. What makes it fast is
;        that it only has to find the original offset by multipling XSIZE by
;        y1.  From then on it increments a pointer to find the next location
;        in screen memory.

		mov put_mode,al  ; save the put image pixel writing mode
		mov buf_ptr,bx   ; pointer to the buffer where we'll store image

		mov ax,es:[bx]
		add ax,cx   ; add x1 plut xsize of image window

		mov bx,es:[bx +2]
;		mov bx,di
		add bx,dx   ; add y1 plus ysize of image window


		mov x1,cx
		mov y1,dx
		mov x2,ax
		mov y2,bx

		mov bx,buf_ptr
		add bx,4           ; first word was used for xsize dimension
		mov buf_ptr,bx

		mov ax,x1
		mov bx,y1


		cmp active_mode,1
		je putimage_mode1	; different way to calc pixel offset

		;   Next, setup the starting value of the VGA's hioffset reg
		push ax
		push bx
		push cx

		mov cx,ax           ; load x value
		mov ax,bx           ; load the y value
		mul xsize        ; find the pixel offset
		add ax,cx        ; add inthe x value
		mov di,ax


		;  calculate ho many screen byte are from right edge of box
		;  to left edge of box on next row
		mov ax,max_x
		sub ax,x2
		add ax,x1
		mov next_row,ax

		pop cx
		pop bx
		pop ax


		mov cl,put_mode    ; keep the put_mode in cl for access speed
p_again0:
		call buf_to_screen

		inc di              ; advvance screen mapped memory pointer
		inc ax              ; advance in x dir till at right edge
		cmp ax,x2
		jle p_again0

		mov ax,x1                ; reset to left size of rectangle
		inc bx                   ; go to down to next row
		cmp bx,y2
		jg  puti_done0


	;Since were changing rows we'll find a new hi and low
	; the trick is to add the number of bytes from the right side of the
	; box to the left side of the box  ( note: no multiplies needed )

		add di,next_row

		jmp p_again0



puti_done0:
		ret              ; end of patbar in mode 0


                ; ----------------- putimage for mode 1 ------------------

putimage_mode1:
					; first, make sure x1 < x2 and y1 < y2

		call unlock_vga  ;  keep vga ready to plot pixels


	;        Next, setup the starting value of the VGA's hioffset reg
		push ax
		push bx
		push cx

		mov cx,ax           ; load x value
		mov ax,bx           ; load the y value
		call calc_hilow     ; find the values of hio and lowo
		mov ax,bx           ; load hio
		call set_hioffset   ; set the vga register for the hioffset


		;  calculate ho many screen byte are from right edge of box
		;  to left edge of box on next row
		mov ax,max_x
		sub ax,x2
		add ax,x1
		mov next_row,ax

		pop cx
		pop bx
		pop ax

		mov cl,put_mode     ; keep the put_mode in cl for access speed
p_again1:
		call buf_to_screen

		inc di
		cmp di,0FFFh
		jle p_leave_hio1
		sub di,1000h
		inc lasthio

		push ax            ; let vga card know we incremented hi offset
		mov ax,lasthio
		call set_hioffset
		pop ax

p_leave_hio1:
		inc ax              ; advance in x dir till at right edge
		cmp ax,x2
		jle p_again1

		mov ax,x1                ; reset to left size of rectangle
		inc bx                   ; go to down to next row
		cmp bx,y2
		jg  puti_done1


	;Since were changing rows we'll find a new hi and low
	; the trick is to add the number of bytes from the right side of the
	; box to the left side of the box  ( note: no multiplies needed )

		add di,next_row
		cmp di,0FFFh
		jle p_no_bump1
		sub di,1000h
		inc lasthio
		push ax
		mov ax,lasthio
		call set_hioffset
		pop ax
p_no_bump1:

		jmp p_again1

puti_done1:
		call lock_vga           ; protect the vgas register again

		ret


          ; ----------------------------------------------
          ;    this code write the next byte from the put image buffer
          ;    to the display screen

buf_to_screen proc near
			; move a byte from the image buffer to the screen
		push ax
		push bx

				; get the byte out of the users buffer
		mov bx,buf_ptr
		mov al,byte ptr es:[bx]

		pop bx
		push es
		push ax

		mov ax,0A000h
		mov es,ax        ; es reg is pointer to VGA screen

		pop ax

			; write the byte to screen mem according to users mode
;		mov cl,put_mode
		cmp cl,0
		jne try_xor0
		mov byte ptr es:[di],al
		jmp mode_found0
try_xor0:
		cmp cl,1
		jne try_or0
		xor byte ptr es:[di],al
		jmp mode_found0
try_or0:
		cmp cl,2
		jne try_and0
		or byte ptr es:[di],al
		jmp mode_found0
try_and0:
		cmp cl,3
		jne then_comp0
		and byte ptr es:[di],al
		jmp mode_found0
then_comp0:
		xor al,0FFh               ; complement the byte
		and byte ptr es:[di],al
mode_found0:

		pop es
		pop ax

		inc buf_ptr  ; point to next byte in the users buffer

		ret

buf_to_screen endp


; 40 -------------------------------------------------------------------

; input:
;	ax = x1
;	bx = y1
;	cx = x2
;	dx = y2
; return: nothing

set_clip: ; Set clipping
				; load the limits of the clipping window
		mov clip_x1,ax
		mov clip_y1,bx
		mov clip_x2,cx
		mov clip_y2,dx

		ret



; 3A -------------------------------------------------------------------

; if al = 0
;	input:  nothing
;	return:
;		bx = size of color table
;		cx = maximum color number
; if al = 1
;	input:  nothing
;	return:
;		es:bx = pointer to default color table (first byte is size)

color_query:		; SetParameters

			cmp al,0
			jnz	color_table_q

			mov	bx,256
			mov cx,255
			ret

color_table_q:
			push cs
			pop es
			mov bx,offset color_table
			ret




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

	; line drawing routine from Assembly language
	; primer - The Waite Group - page 339-340

draw_line	proc	near
		mov ax,y2
		sub ax,y1

		mov si,1
		jge	store_y
		mov si,-1
		neg ax

store_y:
		mov delta_y,ax

		mov	ax,x2
		sub ax,x1

		mov	di,1
		jge	store_x
		mov	di,-1
		neg	ax

store_x:
		mov	delta_x,ax

		mov	ax,delta_x
		cmp	ax,delta_y
		jl	csteep
		call easy
		jmp finish_line

csteep:
		call steep

finish_line:
		ret

draw_line	endp


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

easy	proc	near

		mov	ax,delta_x
		shr	ax,1
		mov	halfx,ax

		mov	cx,x1
		mov	dx,y1
		mov bx,0
		mov ax,delta_x
		mov	count,ax

newdot:
		call dotplot
		add	cx,di
		add bx,delta_y
		cmp	bx,halfx
		jle	dcount
		sub	bx,delta_x

		add	dx,si

dcount:
		dec count
		jge	newdot

		ret

easy	endp


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


steep	proc	near

		mov ax,delta_y
		shr	ax,1
		mov	halfy,ax

		mov	cx,x1
		mov	dx,y1

		mov	bx,0
		mov	ax,delta_y
		mov count,ax

newdot2:
		call dotplot
		add	dx,si
		add bx,delta_x
		cmp	bx,halfy
		jle	dcount2
		sub	bx,delta_y

		add	cx,di

dcount2:
		dec	count
		jge	newdot2

		ret

steep	endp


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


dotplot	proc	near

		push	ax    ; save old values
		push	bx
		push	cx
		push	dx

		; check to see if line style pattern dictates a plot or not
		; if active_bit_pattern[line_bit_ptr] = 0 then dont plot a dot

		push ax
		push cx

		mov	ax,active_linestyle
		mov	cl,line_bit_ptr
		shl ax,cl
		and ax,8000h
		cmp ax,8000h

		pop cx
		pop ax

		mov	ax,cx
		mov	bx,dx

		jne no_dot_plot

		mov dl,current_line_color    ; do plot dot, so load line color

		call put_pixel 		; call the pixel writing routine
no_dot_plot:


		inc line_bit_ptr		; move to next bit in pattern
		cmp line_bit_ptr,16     ; are we at the end of the pattern
		jne	no_reset_linestyle  ; if not do nothing
		mov line_bit_ptr,0  ; else start over at the pattern beginning
no_reset_linestyle:

		pop		dx    ; save old values
		pop 	cx
		pop 	bx
		pop 	ax



		ret

dotplot	endp


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

calc_hilow:        ; find hi and low vga offsets for x,y location
			   ; cx is column (x) , ax is row (y)
			   ; uses value in varible 'xsize'
			   ; results are  lowoffset in di and hioffset in bx

		push dx

		mul xsize      ;   offset =(xsize * y)
		add ax,cx	   ;     + x
		jnc nocarry_o
		inc dx         ;   (DxAx + Cx)
nocarry_o:

		mov di,ax
		and di,0FFFh   ;   low offset is lowest 12 bits of offset

		mov cl,12      ;   (hioffset = offset >> 12
		shr ax,cl
		mov cl,4
		shl dx,cl
		or  dx,ax
		mov bx,dx      ;   hioffset saved in bx

		pop dx

		ret


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

unlock_vga:            ; unlock vga registers

		push ax
		push dx

		mov dx,03CEh   ;   vga I/O port
		mov ax,050Fh
		out dx,ax      ;   unlock vga registers

		pop dx
		pop ax

		ret


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

lock_vga:            ; lock vga registers

		push ax
		push dx

		mov dx,03CEh   ;   vga I/O port
		mov ax,000Fh
		out dx,ax      ;   unlock vga registers

		pop dx
		pop ax

		ret


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

set_hioffset:          ;  sets the vga hi offset register
			; ax contains the value for HIoffset

		push dx

		mov lasthio,ax

		mov ah,al      ; hioffset goes in ah
		mov al,9       ; function code to set PR0A register

		mov dx,03CEh   ;   vga I/O address
		out dx,ax      ;   set the hioffset value

		pop dx

		ret



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

;   .
endofcode label byte  ; This is the label ref'd in ACODE segment
CODE      ENDS
          END
-- 
Gene Lee               UUCP: ...!amdahl!ems!minnow!lee
Unisys Corporation             
Phone: +1 612 635 3993    CSNET: lee@minnow.SP.Unisys.Com
If not for the courage of the fearless manager, the paycheck would be lost.