; Mode X (320x240, 256 colors) rectangle fill routine. Works on all
; VGAs. Uses slow approach that selects the plane explicitly for each
; pixel. Fills up to but not including the column at EndX and the row
; at EndY. No clipping is performed.
; C near-callable as:
;
;    void FillRectangleX(int StartX, int StartY, int EndX, int EndY,
;	unsigned int PageBase, int Color);

SC_INDEX	equ	03c4h	;Sequence Controller Index
MAP_MASK	equ	02h	;index in SC of Map Mask register
SCREEN_SEG	equ	0a000h	;segment of display memory in mode X
SCREEN_WIDTH	equ	80	;width of screen in bytes from one scan line
				; to the next
parms	struc
	dw	2 dup (?)	;pushed BP and return address
StartX	dw	?		;X coordinate of upper left corner of rect
StartY	dw	?		;Y coordinate of upper left corner of rect
EndX	dw	?		;X coordinate of lower right corner of rect
				; (the row at EndX is not filled)
EndY	dw	?		;Y coordinate of lower right corner of rect
				; (the column at EndY is not filled)
PageBase dw	?		;base offset in display memory of page in
				; which to fill rectangle
Color	dw	?		;color in which to draw pixel
parms	ends

	.model	small
	.code
	public	_FillRectangleX
_FillRectangleX proc	near
	push	bp		;preserve caller's stack frame
	mov	bp,sp		;point to local stack frame
	push	si		;preserve caller's register variables
	push	di

	mov	ax,SCREEN_WIDTH
	mul	[bp+StartY]	;offset in page of top rectangle scan line
	mov	di,[bp+StartX]
	shr	di,1
	shr	di,1		;X/4 = offset of first rectangle pixel in scan
				; line
	add	di,ax		;offset of first rectangle pixel in page
	add	di,[bp+PageBase] ;offset of first rectangle pixel in
				; display memory
	mov	ax,SCREEN_SEG
	mov	es,ax		;point ES:DI to the first rectangle pixel's
				; address
	mov	dx,SC_INDEX	;set the Sequence Controller Index to
	mov	al,MAP_MASK	; point to the Map Mask register
	out	dx,al
	inc	dx		;point DX to the SC Data register
	mov	cl,byte ptr [bp+StartX]
	and	cl,011b		;CL = first rectangle pixel's plane
	mov	al,01h
	shl	al,cl		;set only the bit for the pixel's plane to 1
	mov	ah,byte ptr [bp+Color] ;color with which to fill
	mov	bx,[bp+EndY]
	sub	bx,[bp+StartY]	;BX = height of rectangle
	jle	FillDone	;skip if 0 or negative height
	mov	si,[bp+EndX]
	sub	si,[bp+StartX]	;CX = width of rectangle
	jle	FillDone	;skip if 0 or negative width
FillRowsLoop:
	push	ax		;remember the plane mask for the left edge
	push	di		;remember the start offset of the scan line
	mov	cx,si		;set count of pixels in this scan line
FillScanLineLoop:
	out	dx,al		;set the plane for this pixel
	mov	es:[di],ah	;draw the pixel
	shl	al,1		;adjust the plane mask for the next pixel's
	and	al,01111b	; bit, modulo 4
	jnz	AddressSet	;advance address if we turned over from
	inc	di		; plane 3 to plane 0
	mov	al,00001b	;set plane mask bit for plane 0
AddressSet:
	loop	FillScanLineLoop
	pop	di		;retrieve the start offset of the scan line
	add	di,SCREEN_WIDTH ;point to the start of the next scan
				; line of the rectangle
	pop	ax		;retrieve the plane mask for the left edge
	dec	bx		;count down scan lines
	jnz	FillRowsLoop
FillDone:
	pop	di		;restore caller's register variables
	pop	si
	pop	bp		;restore caller's stack frame
	ret
_FillRectangleX endp
	end
