; Mode X (320x240, 256 colors) display memory to display memory copy
; routine. Left edge of source rectangle modulo 4 must equal left edge
; of destination rectangle modulo 4. Works on all VGAs. Uses approach
; of reading 4 pixels at a time from the source into the latches, then
; writing the latches to the destination. Copies up to but not
; including the column at SourceEndX and the row at SourceEndY. No
; clipping is performed. Results are not guaranteed if the source and
; destination overlap. C near-callable as:
;
;    void CopyScreenToScreenX(int SourceStartX, int SourceStartY,
;	int SourceEndX, int SourceEndY, int DestStartX,
;	int DestStartY, unsigned int SourcePageBase,
;	unsigned int DestPageBase, int SourceBitmapWidth,
;	int DestBitmapWidth);

SC_INDEX	equ	03c4h		;Sequence Controller Index register port
MAP_MASK	equ	02h		;index in SC of Map Mask register
GC_INDEX	equ	03ceh		;Graphics Controller Index register port
BIT_MASK	equ	08h		;index in GC of Bit Mask register
SCREEN_SEG	equ	0a000h		;segment of display memory in Mode X

parms	struc
		dw	2 dup (?)	;pushed BP and return address
SourceStartX	dw	?		;X coordinate of upper left corner of source
SourceStartY	dw	?		;Y coordinate of upper left corner of source
SourceEndX	dw	?		;X coordinate of lower right corner of source
					; (the row at SourceEndX is not copied)
SourceEndY	dw	?		;Y coordinate of lower right corner of source
					; (the column at SourceEndY is not copied)
DestStartX	dw	?		;X coordinate of upper left corner of dest
DestStartY	dw	?		;Y coordinate of upper left corner of dest
SourcePageBase	dw	?		;base offset in display memory of page in
					; which source resides
DestPageBase	dw	?		;base offset in display memory of page in
					; which dest resides
SourceBitmapWidth dw	?		;# of pixels across source bitmap
					; (must be a multiple of 4)
DestBitmapWidth	  dw	?		;# of pixels across dest bitmap
					; (must be a multiple of 4)
parms	ends

SourceNextScanOffset equ -2		;local storage for distance from end of
					; one source scan line to start of next
DestNextScanOffset   equ -4		;local storage for distance from end of
					; one dest scan line to start of next
RectAddrWidth	     equ -6		;local storage for address width of rectangle
Height		     equ -8		;local storage for height of rectangle
STACK_FRAME_SIZE     equ  8

	.model	small
	.data
; Plane masks for clipping left and right edges of rectangle.
LeftClipPlaneMask	db	00fh,00eh,00ch,008h
RightClipPlaneMask	db	00fh,001h,003h,007h
	.code
	public	_CopyScreenToScreenX
_CopyScreenToScreenX proc    near
	push	bp			;preserve caller's stack frame
	mov	bp,sp			;point to local stack frame
	sub	sp,STACK_FRAME_SIZE	;allocate space for local vars
	push	si			;preserve caller's register variables
	push	di
	push	ds

	cld
	mov	dx,GC_INDEX		;set the bit mask to select all bits
	mov	ax,00000h+BIT_MASK	; from the latches and none from
	out	dx,ax			; the CPU, so that we can write the
					; latch contents directly to memory
	mov	ax,SCREEN_SEG		;point ES to display memory
	mov	es,ax
	mov	ax,[bp+DestBitmapWidth]
	shr	ax,1			;convert to width in addresses
	shr	ax,1
	mul	[bp+DestStartY]		;top dest rect scan line
	mov	di,[bp+DestStartX]
	shr	di,1			;X/4 = offset of first dest rect pixel in
	shr	di,1			; scan line
	add	di,ax			;offset of first dest rect pixel in page
	add	di,[bp+DestPageBase]	;offset of first dest rect pixel
					; in display memory
	mov	ax,[bp+SourceBitmapWidth]
	shr	ax,1			;convert to width in addresses
	shr	ax,1
	mul	[bp+SourceStartY]	;top source rect scan line
	mov	si,[bp+SourceStartX]
	mov	bx,si
	shr	si,1			;X/4 = offset of first source rect pixel in
	shr	si,1				; scan line
	add	si,ax			;offset of first source rect pixel in page
	add	si,[bp+SourcePageBase]	;offset of first source rect
					; pixel in display memory
	and	bx,0003h		;look up left edge plane mask
	mov	ah,LeftClipPlaneMask[bx]; to clip
	mov	bx,[bp+SourceEndX]
	and	bx,0003h		;look up right edge plane
	mov	al,RightClipPlaneMask[bx] ; mask to clip
	mov	bx,ax			;put the masks in BX

	mov	cx,[bp+SourceEndX]	;calculate # of addresses across
	mov	ax,[bp+SourceStartX]	; rect
	cmp	cx,ax
	jle	CopyDone		;skip if 0 or negative width
	dec	cx
	and	ax,not 011b
	sub	cx,ax
	shr	cx,1
	shr	cx,1			;# of addresses across rectangle to copy - 1
	jnz	MasksSet		;there's more than one address to draw
	and	bh,bl			;there's only one address, so combine the left
					; and right edge clip masks
MasksSet:
	mov	ax,[bp+SourceEndY]
	sub	ax,[bp+SourceStartY]	;AX = height of rectangle
	jle	CopyDone		;skip if 0 or negative height
	mov	[bp+Height],ax
	mov	ax,[bp+DestBitmapWidth]
	shr	ax,1			;convert to width in addresses
	shr	ax,1
	sub	ax,cx			;distance from end of one dest scan line to
	dec	ax			; start of next
	mov	[bp+DestNextScanOffset],ax
	mov	ax,[bp+SourceBitmapWidth]
	shr	ax,1			;convert to width in addresses
	shr	ax,1
	sub	ax,cx			;distance from end of one source scan line to
	dec	ax			; start of next
	mov	[bp+SourceNextScanOffset],ax
	mov	[bp+RectAddrWidth],cx	;remember width in addresses - 1
;-----------------------BUG FIX
	mov	dx,SC_INDEX
	mov	al,MAP_MASK
	out	dx,al			;point SC Index reg to Map Mask
	inc	dx			;point to SC Data reg
;-----------------------BUG FIX
	mov	ax,es			;DS=ES=screen segment for MOVS
	mov	ds,ax
CopyRowsLoop:
	mov	cx,[bp+RectAddrWidth]	;width across - 1
	mov	al,bh			;put left-edge clip mask in AL
	out	dx,al			;set the left-edge plane (clip) mask
	movsb				;copy the left edge (pixels go through
					; latches)
	dec	cx			;count off left edge address
	js	CopyLoopBottom		;that's the only address
	jz	DoRightEdge		;there are only two addresses
	mov	al,00fh			;middle addresses are drawn 4 pixels at a pop
	out	dx,al			;set the middle pixel mask to no clip
	rep	movsb			;draw the middle addresses four pixels apiece
					; (pixels copied through latches)
DoRightEdge:
	mov	al,bl			;put right-edge clip mask in AL
	out	dx,al			;set the right-edge plane (clip) mask
	movsb				;draw the right edge (pixels copied through
					; latches)
CopyLoopBottom:
	add	si,[bp+SourceNextScanOffset] ;point to the start of
	add	di,[bp+DestNextScanOffset]   ; next source & dest lines
	dec	word ptr [bp+Height]	;count down scan lines
	jnz	CopyRowsLoop
CopyDone:
	mov	dx,GC_INDEX+1		;restore the bit mask to its default,
	mov	al,0ffh			; which selects all bits from the CPU
	out	dx,al			; and none from the latches (the GC
					; Index still points to Bit Mask)
	pop	ds
	pop	di			;restore caller's register variables
	pop	si
	mov	sp,bp			;discard storage for local variables
	pop	bp			;restore caller's stack frame
	ret
_CopyScreenToScreenX endp
	end
