
;SBIT.INC - Copyright 1991-1994 Knight Software
;  History:
;     17 May 1991 - First release 
;     22 Nov 1992 - Adapted for protected mode
;     15 Dec 1992 - Fixed bug in WriteFillLine
;                    non-pattern fill was always doing MoveWrite
;     17 Apr 1993 - Added animation enhancement to GetImage
;     20 Apr 1994 - Fixed minor bug in GetImage animation
;                   Added PutImage low-flicker animation
;     28 Aug 1994 - Finally got PutImage animation working
;
;-----------------------------------------------------------
;Assume:  DS = data segment
;Entry:   DX = Count - number bytes to write
;Return:  N/A
;Destroy: None

WriteClear PROC  NEAR	;called by ClearDevice
	PUSH	ES
	PUSH	DI
	PUSH	CX
	PUSH	AX
	MOV	ES,DS:[VideoSegment] ;video is at segment 0a000h
	MOV	DI,DS:[PixelAddress] ;destination offset address
	MOV	AL,DS:[InitColor]    ;preload init color
	MOV	AH,AL		     ;extend color for word write
	MOV	CX,DX		     ;copy length in bytes to CX
	CLD
	SHR	CX,1		     ;and convert to words
	JZ	@WriteClearOdd       ;nothing else to write if done
	REP	STOSW		     ;fast write to the screen
@WriteClearOdd:
	JNC	@WriteClearDone      ;if odd count do the odd byte
	STOSB			     ;ES:DI = dest array		  
@WriteClearDone:
	POP	AX
	POP	CX
	POP	DI
	POP	ES
	RET
WriteClear ENDP


;-----------------------------------------------------------
;Assume:  DS       = data segment
;Entry:   DX       = Count - number bytes to write
;         [PixelX] = offset into scan line to fill (X)
;         [PixelY] = scan line to fill (Y)
;Return:  N/A
;Destroy: None

WriteFillLine PROC  NEAR	;called by BAR
	PUSH	DS
	PUSH	ES
	PUSH	SI
	PUSH	DI
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	AX

	MOV	ES,DS:[VideoSegment]  ;video is at segment 0a000h
	MOV	DI,DS:[PixelAddress]  ;destination offset address
	MOV	SI,OFFSET FillPattern ;point to fill pattern table
	MOV	AX,DS:[PixelY]
	AND	AX,07H
	ADD	SI,AX		      ;can we do it fast?
	MOV	AL,DS:[FillForeColor] ;preload foreground color
	MOV	AH,DS:[FillBackColor] ;preload background color

	MOV	CL,DS:[FillPixelWriteMode] ;preload fill write mode
	TEST	CL,03H		      ;not a fast type fill
	JNZ	@WriteSlowFillLine    ;so we gotta do it the slow way
	MOV	CH,DS:[SI]	      ;get pattern to use
	INC	CH		      ;if pattern = 0FFH, do fast
	JZ	@WriteForeFastFillLine
	DEC	CH		      ;if pattern = 00H, do fast
	JZ	@WriteBackFastFillLine;if not fast pattern, do it slow

@WriteSlowFillLine:
	MOV	CX,DS:[PixelX]	      ;gotta do it slow 
	MOV	CH,8		      ;get PixelX location
	AND	CL,07H		      ;and sync pattern to it
	SUB	CH,CL
	MOV	BL,DS:[SI]
	SHL	BL,CL
	MOV	CL,BL		      ;copy pattern to CL
;	MOV	AL,DS:[FillForeColor] ;preload foreground color
;	MOV	AH,DS:[FillBackColor] ;preload background color
	MOV	BX,DS:[FillPixelProc] ;load pixel calling routine

@WriteAltFillLineLp:
	TEST	CL,80H		      ;set flags for call
	CALL	BX		      ;via table proc pointer
	INC	DI		      ;and loop till done
	DEC	DX
	JZ	@WriteFillLineDone
	SHL	CL,1
	DEC	CH		      ;do next pattern bit
	JNZ	@WriteAltFillLineLp
	MOV	CL,DS:[SI]	      ;reload pattern byte
	MOV	CH,8		      ;and count length
	JMP	SHORT @WriteAltFillLineLp ;and do again

@WriteForeFastFillLine:		   ;AH:AL has color to write
	TEST	CL,04H
	JZ	@WriteForeFastFill2
	NOT	AL		   ;invert color if NOT write
@WriteForeFastFill2:
	TEST	CL,10H		   ;ok to write foreground?
	JZ	@WriteFastFillLine ;yes, go do it
	JMP	@WriteFillLineDone ;nope, so nothing to do

@WriteBackFastFillLine:		   ;AH:AL has color to write
	TEST	CL,04H
	JZ	@WriteBackFastFill2
	NOT	AH		   ;invert color if NOT write
@WriteBackFastFill2:
	MOV	AL,AH		   ;we will be writing back color
	TEST	CL,08H		   ;ok to write background?
	JZ	@WriteFastFillLine ;yes, go do it
	JMP	@WriteFillLineDone ;nope, so do nothing

@WriteFastFillLine:
	MOV	AH,AL		   ;extend color for word write
	MOV	CX,DX		   ;copy length in bytes to CX
	CLD
	SHR	CX,1		   ;and convert to words
	JZ	@WriteFillLineOdd  ;nothing else to write if done
	REP	STOSW		   ;fast fill write to the screen
@WriteFillLineOdd:
	JNC	@WriteFillLineDone ;if odd count do the odd byte
	STOSB			   ;ES:DI = dest array		  

@WriteFillLineDone:
	POP	AX
	POP	BX
	POP	CX
	POP	DX
	POP	DI
	POP	SI
	POP	ES
	POP	DS
	RET
WriteFillLine ENDP


;-----------------------------------------------------------
;Assume:   DS    = data segment
;Entry:    DX    = Count - number bytes to read
;	   ES:SI = Pointer to cpu memory 
;Return:   N/A
;Destorys: None

ReadBitMap PROC  NEAR	;(Called by GetImage only)
	PUSH	ES
	PUSH	DS
	PUSH	SI
	PUSH	DI
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	AX

	MOV	CX,DX
	CMP	DS:[GetImageSwapFlag],0
	JZ	@ReadBitMapImage
	MOV	AL,DS:[GetImagePixelWriteMode]
	OR	AL,AL
	JZ	@SwapBitMapFast

	MOV	AH,DS:[GetImageBackColor] ;AH = background color
	MOV	BX,DS:[GetImagePixelProc] ;BX = pixel write method
	MOV	DI,DS:[PixelAddress]      ;ES:DI = source array (video)
	PUSH	AX		 	  ;DS:SI = dest array (cpu)
	MOV	AX,ES
	MOV	ES,DS:[VideoSegment] ;video is at segment 0a000h
	MOV	DS,AX
	POP	AX
@SwapBitMapSlow:
	MOV	AL,ES:[DI]	     ;get display byte
	XCHG	DS:[SI],AL	     ;swap with cpu memory
	CMP	AL,AH		     ;are we drawing background color?
	CALL	BX		     ;write pixel to the display (ES:DI)
	INC	DI
	INC	SI
	LOOP	@SwapBitMapSlow
	JMP	SHORT @ReadBitMapDone

@SwapBitMapFast:
	MOV	DI,SI
	MOV	SI,DS:[PixelAddress] ;DS:SI = source array (video)
	MOV	DS,DS:[VideoSegment] ;video is at segment 0a000h
	SHR	CX,1		     ;ES:DI = dest array (cpu)
	JNC	@SwapBitMapEven
	MOV	AL,DS:[SI]	     ;get display byte
	XCHG	ES:[DI],AL	     ;swap with cpu memory
	MOV	DS:[SI],AL	     ;write new display byte
	JZ	@ReadBitMapDone	     ;nothing else to draw if done
	INC	DI
	INC	SI
@SwapBitMapEven:
	MOV	AX,DS:[SI]	     ;get display word
	XCHG	ES:[DI],AX	     ;swap with cpu memory
	MOV	DS:[SI],AX	     ;write new display word 
	INC	DI
	INC	DI
	INC	SI
	INC	SI
	LOOP	@SwapBitMapEven
	JMP	SHORT @ReadBitMapDone

@ReadBitMapImage:
	CLD
	MOV	DI,SI
	MOV	SI,DS:[PixelAddress] ;DS:SI = source array
	MOV	DS,DS:[VideoSegment] ;video is at segment 0a000h
	SHR	CX,1		     ;ES:DI = dest array
	JZ	@ReadBitMapOdd	     ;nothing else to draw if done
	REP	MOVSW		     ;read it from the screen
@ReadBitMapOdd:
	JNC	@ReadBitMapDone
	MOVSB			     ;if odd count do the odd byte
@ReadBitMapDone:

	POP	AX
	POP	BX
	POP	CX
	POP	DX
	POP	DI
	POP	SI
	POP	DS
	POP	ES
	RET
ReadBitMap ENDP


;-----------------------------------------------------------
;Assume: DS    = data segment
;Entry:  DX    = Count - number bytes to write
;	 ES:SI = Pointer to cpu memory 

WriteBitMap PROC  NEAR		;(called by PutImage only)
	PUSH	DS
	PUSH	ES
	PUSH	SI
	PUSH	DI
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	AX
	MOV	CX,DX
	MOV	AL,DS:[PutImagePixelWriteMode]
	MOV	AH,DS:[PutImageBackColor]  ;preload background color
	MOV	BX,DS:[PutImagePixelProc]
	MOV	DI,DS:[PixelAddress]       ;destination offset address
	PUSH	AX
	MOV	AX,ES
	MOV	ES,DS:[VideoSegment]     ;video is at segment 0a000h
	MOV	DS,AX		         ;DS:SI = source array
	POP	AX
	OR	AL,AL
	JZ	@WriteBitMapFast         ;if mode zero, use fast move
@WriteAltBitMapLp:
	MOV	AL,DS:[SI]	  ;get next pixel from buffer
	CMP	AL,AH		  ;is this bitmap background color?
	CALL	BX		  ;if other style write mode (ES:DI^)
	INC	SI		  ;use long call process
	INC	DI		  ;via table proc pointer
	LOOP	@WriteAltBitMapLp ;loop til done
	JMP	SHORT @WriteBitMapDone
@WriteBitMapFast:
	CLD
	SHR	CX,1		 ;ES:DI = dest array
	JZ	@WriteBitMapOdd  ;if nothing else to write all done
	REP	MOVSW		 ;fast block write to the screen
@WriteBitMapOdd:
	JNC	@WriteBitMapDone ;DS:SI = source array
	MOVSB			 ;if odd count do the odd byte

@WriteBitMapDone:
	POP	AX
	POP	BX
	POP	CX
	POP	DX
	POP	DI
	POP	SI
	POP	ES
	POP	DS
	RET
WriteBitMap  ENDP


;-------------------------------------------------------
;Bit map read/write entry code
;BitMapProc ptr = pointer to read/write procedure to call
;To write blockfill, call as: LEA DI,WriteFillLine; CALL DoBitMap (bar)
;To write bit map, call as:   LEA DI,WriteBitMap;   CALL DoBitMap (putimage)
;To read bit map, call as:    LEA DI,ReadBitMap;    CALL DoBitMap (getimage)
;To clear bit map, call as:   LEA DI,WriteClear;    CALL DoBitMap (cleardevice)
;Assume:   DS    = data segment
;Enter:    ES:SI = cpu memory pointer (not needed for WriteFillLine)
;	   DS:DI = operation procedure pointer
;          CX    = start X location
;          DX    = start Y location
;          AX    = image width (x size)
;          BX    = image height (y size)
;Return:   N/A
;Destroys: None

DoBitMap PROC NEAR
	PUSH	ES
	PUSH	DI
	PUSH	SI
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	AX

	MOV	DS:[BitMapProc],DI ;save proc ptr
	MOV	DS:[ImageW],AX	   ;AX= Image Width
	MOV	DS:[ImageX],CX	   ;CX= Image X1
	MOV	DS:[PixelY],DX	   ;DX= Image Y1
	MOV	CX,BX		   ;BX= Image Height

@DoBitMapLp:
	MOV	AX,DS:[ImageX]
	MOV	DS:[PixelX],AX	   	   ;start at left edge 

	MOV	DX,DS:[ImageW]
	CALL	GetPixelAddress		   ;locate the start adr
	CMP	DX,DS:[PixelSegmentLength] ;if larger than segment
	JC 	@SingleBitScan		   ;split the scan line op
	MOV	DX,DS:[PixelSegmentLength] ;do the first part
	CALL	WORD PTR DS:[BitMapProc]   ;of the scan line
	MOV	AX,DS:[PixelSegmentLength] ;start where we left off
	ADD	DS:[PixelX],AX		   ;on the screen
	MOV	DX,DS:[ImageW]		   ;reduce count by previous
	SUB	DX,AX			   ;amount processed
	ADD	SI,AX			   ;adj cpu memory pointer
	CALL	GetPixelAddress		   ;get the new start adr
@SingleBitScan:
	OR	DX,DX			   ;anything to do?
	JZ	@DoBitMapNext		   ;nope, so we're done
	CALL	WORD PTR DS:[BitMapProc]   ;process the scan line
	ADD	SI,DX		           ;adj cpu memory pointer
@DoBitMapNext:
	INC	WORD PTR DS:[PixelY]	   ;adj PixelY pointer
	LOOP	@DoBitMapLp		   ;loop til all are done
@DoBitMapExit:
	POP	AX
	POP	BX
	POP	CX
	POP	DX
	POP	SI
	POP	DI
	POP	ES
	RET
DoBitMap ENDP


;-------------------------------------------------------
;Animation Bit map entry code (PutImage - type $A0)
;Assume:   DS    = data segment
;Enter:    ES:SI = pointer to animation bitmap record
;          CX    = Image X location
;          DX    = Image Y location
;          ES:[SI+0] = Image Width
;          ES:[SI+2] = Image Height
;          ES:[SI+4] = Old bkg X location
;          ES:[SI+6] = Old bkg Y location 
;          ES:[SI+8] = Ptr to master image 
;          ES:[SI+12] = Ptr to saved image (bkg) 
;Return:   N/A
;Destroys: None
;
;If DS:[PutImageType]=0A0H a special animation version of PutImage 
;is used. Specifically, ES:SI points to an array of information.
;The first four words contain the Width, Height, Old X and Old Y 
;information about the sprite. Following that are two pointers.
;The first one points at the master Image to be displayed, and the
;second one points at the previously saved background image.
;The master image and saved image are in standard GetImage/PutImage
;BGI format. This means that the width and height information are
;stored in the first two words. Note that this duplicates the 
;information in the first two words of the the animation record.
;The reason for this is because the high level Graph PutImage code
;value checks the width and height to verify that they are valid, and
;will clip the values if they are not. So the information was duplicated
;in the animation record to maintain compatibility. However, this 
;information is not actually used. The Image width and height is 
;assumed to be in the bitmap as it normally would be. The bitmap images
;are in the standard BGI format so that a normal GetImage/PutImage 
;call can use the bitmaps if so desired.
;Note: You must call a standard GetImage once using the Background 
;Image bitmap before using this function in order for the Background
;bitmap to contain valid information. You must also fill the OldX and 
;OldY variables in the record with the location of the image that you 
;read with getImage. Alternately, you can put a $8000 in the Height
;variable to tell the animation code that the image was never read. 
;In this case, the background will not be restored since it isn't valid.

DoAniBitMap PROC NEAR
	PUSH	ES
	PUSH	DI
	PUSH	SI
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	AX

;Animation Bitmap Setup code
	MOV	DS:[ImageX],CX	   ;save Image X location
	MOV	DS:[ImageY],DX     ;save Image Y location
	MOV	AX,ES:[SI+4]	   ;OldX
	MOV	BX,ES:[SI+6]	   ;OldY
	MOV	ES:[SI+4],CX	   ;update bkg XY (OldX/OldY) info
	MOV	ES:[SI+6],DX
	CMP	AX,8000H	   ;if OldX or OldY = 8000H
	JZ	@AniBeg3	   ;no back ground available
	CMP	BX,8000H
	JZ	@AniBeg3
	MOV	DS:[BImageX],AX	   ;save old background X/Y loc
	MOV	DS:[BImageY],BX
	MOV	byte ptr DS:[BkGndAvail],1
	JMP	@AniBeg5
@AniBeg3:
	MOV	AX,0
	MOV	DS:[BImageX],AX
	MOV	DS:[BImageY],AX
	MOV	byte ptr DS:[BkGndAvail],0

@AniBeg5:
	PUSH	DS
	LDS	DI,DWORD PTR ES:[SI+8]
	MOV	CX,WORD PTR DS:[DI]	;get image width 
	MOV	DX,WORD PTR DS:[DI+2]	;get image height
	ADD	DI,4			;point to putimage start
	MOV	AX,DS
	POP	DS
	MOV	WORD PTR DS:[ImagePtr],DI   ;save putImage ptr
	MOV	WORD PTR DS:[ImagePtr+2],AX
	INC	CX
	INC	DX
	MOV	DS:[ImageW],CX		;save image Width
	MOV	DS:[ImageH],DX		;save Image Height

	DEC	CX
	DEC	DX
	MOV	AL,DS:[BkGndAvail]
	PUSH	DS
	LDS	DI,DWORD PTR ES:[SI+12]
	OR	AL,AL			;if no bkg available
	JZ	@AniBeg7		;copy new image width/height
	MOV	WORD PTR DS:[DI],CX	;update old bkg width
	MOV	WORD PTR DS:[DI+2],DX	;update old bkg height
@AniBeg7:
	MOV	CX,WORD PTR DS:[DI]	;get old background width
	MOV	DX,WORD PTR DS:[DI+2]	;get old background height
	ADD	DI,4			;point to old bkg image start
	MOV	AX,DS
	POP	DS
	MOV	WORD PTR DS:[OldBkgPtr],DI ;save old Bkg img ptr
	MOV	WORD PTR DS:[OldBkgPtr+2],AX
	MOV	WORD PTR DS:[NewBkgPtr],DI ;save new Bkg img ptr
	MOV	WORD PTR DS:[NewBkgPtr+2],AX
	INC	CX
	INC	DX
	MOV	DS:[BImageW],CX		;save bkg image Width
	MOV	DS:[BImageH],DX		;save bkg Image Height

	CMP	byte ptr DS:[BkGndAvail],0 ;if no background image
	JNZ	@AniChk2		   ;clr bkg vars
@AniChk1:
	MOV	WORD PTR DS:[BImageW],0	;zero bkg width = nothing
	MOV	WORD PTR DS:[BImageH],0	;zero bkg height = nothing
	MOV	WORD PTR DS:[BImageX],0
	MOV	WORD PTR DS:[BImageY],0
	JMP	@AniChk3
@AniChk2:
	MOV	CX,DS:[BImageX]
	MOV	DX,DS:[BImageY]
	CMP	CX,DS:[ImageX]
	JNZ	@AniChk3		;if not in same location, 
	CMP	DX,DS:[ImageY]		;update the image
	JZ	@AniExit		;if same loc, nothing to do
@AniChk3:
	CALL	AniLoop		;process image fwd dir
@AniExit:
	POP	AX
	POP	BX
	POP	CX
	POP	DX
	POP	SI
	POP	DI
	POP	ES
	RET
DoAniBitMap ENDP


;Animation bitmap forward transfer code
AniLoop:
	CMP	WORD PTR DS:[BImageH],0    ;anything to do?
	JZ	@AniLoop4
	CALL	AniBackRestore		   ;yes, restore a line
	INC	WORD PTR DS:[BImageY]	   ;adj BImageY pointer
	DEC	WORD PTR DS:[BImageH]	   ;adj bkg img height cnt
	MOV	AX,DS:[BImageW]
	ADD	WORD PTR DS:[OldBkgPtr],AX ;update pointer

@AniLoop4:
	CMP	WORD PTR DS:[BImageH],0	;If bkg done, always write
	JZ	@AniLoop6

	MOV	AX,DS:[ImageY]
	MOV	BX,DS:[BImageY]
	CMP	AX,BX			;is IMG Y < BKG Y?
	JNC	@AniLoop8		;no, don't write yet
@AniLoop6:
	CMP	WORD PTR DS:[ImageH],0	;if img done, bypass
	JZ	@AniLoop8
	CALL	AniWriteImage		;yes, write a line
	INC	WORD PTR DS:[ImageY]	;adj img Y ptr
	DEC	WORD PTR DS:[ImageH]	;adj img height cnt

	MOV	AX,DS:[ImageW]
	ADD	WORD PTR DS:[ImagePtr],AX  ;adj image mem pointer
	ADD	WORD PTR DS:[NewBkgPtr],AX ;adj bkg mem pointer

@AniLoop8:
	MOV	AX,DS:[BImageH]
	OR	AX,DS:[ImageH]		;are we done yet?
	JNZ	AniLoop			;nope, keep trying
	RET


;Restore the background image if appropriate
AniBackRestore PROC NEAR
	LES	SI,DS:[OldBkgPtr]  ;get old background ptr
	MOV	DX,DS:[BImageY]
	MOV	DS:[PixelY],DX	   ;Update PixelY
	MOV	AX,DS:[BImageX]
	MOV	DS:[PixelX],AX	   ;start at left edge 
	MOV	DX,DS:[BImageW]
	CALL	GetPixelAddress		   ;locate the start adr
	CMP	DX,DS:[PixelSegmentLength] ;if larger than segment
	JC 	@SingleBackScan	   	   ;split the scan line op
	MOV	DX,DS:[PixelSegmentLength] ;do the first part
	CALL	CopyAniBits		   ;of the scan line
	MOV	AX,DS:[PixelSegmentLength] ;start where we left off
	ADD	DS:[PixelX],AX		   ;on the screen
	MOV	DX,DS:[BImageW]		   ;reduce count by previous
	SUB	DX,AX			   ;amount processed
	ADD	SI,AX			   ;adj cpu memory pointer
	CALL	GetPixelAddress		   ;get the new start adr
@SingleBackScan:
	OR	DX,DX			   ;anything to do?
	JZ	@AniBackExit		   ;nope, we're done
	CALL	CopyAniBits		   ;process the scan line
;	ADD	SI,DX		           ;adj cpu memory pointer
;	MOV	WORD PTR DS:[OldBkgPtr],SI ;update pointer
@AniBackExit:
	RET
AniBackRestore ENDP

CopyAniBits PROC NEAR
	PUSH	ES
	PUSH	DS
	PUSH	SI
	PUSH	DI
	MOV	CX,DX
	MOV	AX,ES
	MOV	DI,DS:[PixelAddress] ;DS:SI = source array
	MOV	ES,DS:[VideoSegment] ;video is at segment 0a000h
	MOV	DS,AX
	CLD
	SHR	CX,1		     ;ES:DI = dest array
	JZ	@CopyAniBitsOdd      ;nothing else to draw if done
	REP	MOVSW		     ;read it from the screen
@CopyAniBitsOdd:
	JNC	@CopyAniBitsDone
	MOVSB			     ;if odd count do the odd byte
@CopyAniBitsDone:
	POP	DI
	POP	SI
	POP	DS
	POP	ES
	RET
CopyAniBits ENDP


;write the new image to the display (saving the bkgrnd as we go)
AniWriteImage PROC NEAR
	MOV	DX,DS:[ImageY]
	MOV	DS:[PixelY],DX	   ;Update PixelY
	MOV	AX,DS:[ImageX]
	MOV	DS:[PixelX],AX	   ;start at left edge 
	MOV	DX,DS:[ImageW]

	MOV	SI,WORD PTR DS:[ImagePtr]  ;SI Image Ptr
	MOV	BX,WORD PTR DS:[NewBkgPtr] ;BX Bkg Ptr
	CALL	GetPixelAddress		   ;locate the start adr
	CMP	DX,DS:[PixelSegmentLength] ;if larger than segment
	JC 	@SingleAniWriteScan	   ;split the scan line op
	MOV	DX,DS:[PixelSegmentLength] ;do the first part
	CALL	SwapAniBits		   ;of the scan line
	MOV	AX,DS:[PixelSegmentLength] ;start where we left off
	ADD	DS:[PixelX],AX		   ;on the screen
	MOV	DX,DS:[ImageW]		   ;reduce count by previous
	SUB	DX,AX			   ;amount processed
	ADD	SI,AX			   ;adj img ptr
	ADD	BX,AX			   ;adj bkg ptr

;	ADD	WORD PTR DS:[ImagePtr],AX  ;adj image mem pointer
;	ADD	WORD PTR DS:[NewBkgPtr],AX ;adj bkg mem pointer
	CALL	GetPixelAddress		   ;get the new start adr
@SingleAniWriteScan:
	OR	DX,DX			   ;anything to do?
	JZ	@AniWriteExit		   ;nope, so get out of here
	CALL	SwapAniBits		   ;process the scan line
;	ADD	WORD PTR DS:[ImagePtr],DX  ;adj image mem pointer
;	ADD	WORD PTR DS:[NewBkgPtr],DX ;adj bkg mem pointer
@AniWriteExit:
	RET
AniWriteImage ENDP


SwapAniBits PROC  NEAR
	PUSH	ES
	PUSH	DS	;enter with SI=ImagePtr
	PUSH	BP	;           BX=NewBkgPtr 
	PUSH	SI
	PUSH	DI
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	AX

	MOV	CX,DX		     	   ;CX = count
	MOV	AL,DS:[PutImagePixelWriteMode]
	OR	AL,AL

	JZ	@SwapAniBitMapFast
;@
	MOV	BP,SI			   ;copy img ptr to BP
	MOV	SI,BX			   ;copy bkg ptr to SI
	MOV	DI,DS:[PixelAddress] 	   ;DS:SI = source array (video)
	MOV	ES,DS:[VideoSegment] 	   ;video is at segment 0a000h
	MOV	AH,DS:[PutImageBackColor]  ;AH = background color
	MOV	BX,DS:[PutImagePixelProc]  ;BX = pixel write method
;	MOV	SI,WORD PTR DS:[NewBkgPtr] ;SI Bkg Ptr
;	MOV	BP,WORD PTR DS:[ImagePtr]  ;BP Image Ptr
	MOV	DX,DS			   ;save cur DS in DX
@SwapAniBitMapSlow:
	MOV	DS,DX
	MOV	DS,WORD PTR DS:[NewBkgPtr+2]
	MOV	AL,ES:[DI]	;get display byte
	MOV	DS:[SI],AL	;save it to bkg array
	MOV	DS,DX
	MOV	DS,WORD PTR DS:[ImagePtr+2]
	MOV	AL,DS:[BP]	;get image byte to write
	CMP	AL,AH		;are we drawing background color?
	CALL	BX		;write pixel to display (ES:DI,AH/AL)
	INC	DI		    
	INC	SI
	INC	BP
	LOOP	@SwapAniBitMapSlow
	JMP	SHORT @SwapAniBitMapDone

@SwapAniBitMapFast:
	MOV	DI,DS:[PixelAddress] ;ES:DI = dest array (video)
	MOV	ES,DS:[VideoSegment] ;video is at segment 0a000h
	MOV	DX,WORD PTR DS:[ImagePtr+2]  ;DX:SI Image ptr
;	MOV	SI,WORD PTR DS:[ImagePtr]
	MOV	BP,WORD PTR DS:[NewBkgPtr+2] ;BP:BX Bkg Ptr
;	MOV	BX,WORD PTR DS:[NewBkgPtr]
	CLD
	SHR	CX,1		     ;convert to word length
	JZ	@SwapAniBitMapOdd    ;nothing, so finish up
	PUSHF
@SwapAniBitMapEven:
	MOV	DS,BP		     ;point DS at bkg image
	MOV	AX,ES:[DI]	     ;get display word
	MOV	DS:[BX],AX	     ;save background image
	INC	BX		     ;adj bkg ptr
	INC	BX
	MOV	DS,DX		     ;point DS at src img
	MOVSW			     ;write new display word 
	LOOP	@SwapAniBitMapEven
	POPF
@SwapAniBitMapOdd:
	JNC	@SwapAniBitMapDone
	MOV	DS,BP		     ;point DS at bkg image
	MOV	AL,ES:[DI]	     ;get display word
	MOV	DS:[BX],AL	     ;save background image
	MOV	DS,DX		     ;point DS at src img
	MOVSB			     ;write new display word 

@SwapAniBitMapDone:
	POP	AX
	POP	BX
	POP	CX
	POP	DX
	POP	DI
	POP	SI
	POP	BP
	POP	DS
	POP	ES
	RET
SwapAniBits ENDP
