
Hi!

Here is the wrapped sprite code, as advertised. It is *far* from optimal,
since I had only one wrapped sprite to worry about in the demo it was 
written for. 

It will almost certainly need work to assemble, since it has been taken
from a larger game. If you get *really* stuck, I'm happy to answer e-mail.

It consits of "GLOBALS.INC", "STRUCTS.INC", "VGA.INC", "SPRITES.ASM"

********* Cut Here for Globals.INC ------------------------------------------
;----------------------------------------------------------------------------:
; GLOBALS.INC																 :
;																			 :
;----------------------------------------------------------------------------:

;----------------------------------------------------------------------------:
;			DATA SEGMENT													 :
;----------------------------------------------------------------------------:


SEGMENT Data_Segment DWORD PUBLIC USE32 'DATA'
ENDS	Data_Segment

;----------------------------------------------------------------------------:
;			CODE SEGMENT													 :
;----------------------------------------------------------------------------:


SEGMENT Code_Segment DWORD PUBLIC USE16 'CODE'
ENDS	Code_Segment

;----------------------------------------------------------------------------:
;			VIDEO SEGMENT										 			 :
;----------------------------------------------------------------------------:

SEGMENT Video_Segment AT 0a000h
ENDS	Video_Segment

	    
;----------------------------------------------------------------------------:
; VGA Globals																 :
;----------------------------------------------------------------------------:

	GLOBAL	Test_Card:NEAR,Unchain:NEAR,Set_VGA_Colour:NEAR
	GLOBAL	Enable_VGA_Bit_Plane:NEAR,Copy_Screen_to_VGA:NEAR

    
;----------------------------------------------------------------------------:
; file globals																 :
;----------------------------------------------------------------------------:

	GLOBAL Get_File:NEAR,File_Data:BYTE:0fff0h,File_Name_List:WORD


;----------------------------------------------------------------------------:
; Fatal error globals														 :
;----------------------------------------------------------------------------:

	GLOBAL Fuckup:FAR

;----------------------------------------------------------------------------:
; Sprite Globals															 :
;----------------------------------------------------------------------------:

	GLOBAL	Display_Unclipped_Sprite:NEAR,Erase_Unclipped_Sprite:NEAR
	GLOBAL	Display_Clipped_Sprite:NEAR,Display_Wrapped_Sprite:NEAR
	GLOBAL	Save_Unclipped_Background:NEAR,Display_Unclipped_Block:NEAR
	GLOBAL	Limit_Sprite:NEAR
	GLOBAL	Set_Up_Frame:NEAR,Separate_Sprite_Planes:NEAR
	GLOBAL	Separate_Mask_Planes:NEAR
	GLOBAL	Set_Up_Sprite:NEAR,Reset_Sprite_Data:NEAR
	GLOBAL	Image_Buffer:BYTE:64000
	GLOBAL	Display_Wrapped_Mask:NEAR
    
Sprite_Item_Size = 32
Frame_Item_Size  = 16
    
;----------------------------------------------------------------------------:
; VBlank Globals															 :
;----------------------------------------------------------------------------:

	GLOBAL	VBlank_Service:FAR
	GLOBAL	Screen_Buffer:BYTE:64000

;----------------------------------------------------------------------------:
; Scroll Globals															 :
;----------------------------------------------------------------------------:

	GLOBAL	Scroll_Left:NEAR,Scroll_Right:NEAR


;----------------------------------------------------------------------------:
; Symbols for frames of animation											 :
;----------------------------------------------------------------------------:

Maximum_Sprites = 32
Maximum_Frames = 100
Maximum_Files = Maximum_Frames
	    
;----------------------------------------------------------------------------:
;	Error numbers															 :
;----------------------------------------------------------------------------:

File_Fuckup 		EQU 0
Image_Space_Fuckup	EQU 1

****** Cut here for "STRUCTS.INC" ---------------------------------------------
;----------------------------------------------------------------------------:
; STRUCTS.INC																 :
;																			 :
;----------------------------------------------------------------------------:


;----------------------------------------------------------------------------:
; States of sprite															 :
;----------------------------------------------------------------------------:

Sprite_Flipped_X	EQU 128
Sprite_Flipped_Y	EQU 64
Sprite_Clipped_X	EQU 32
Sprite_Clipped_Y	EQU 16
Sprite_Enabled		EQU 1

;----------------------------------------------------------------------------:
; An item in the Sprite List												 :
;----------------------------------------------------------------------------:
Sprite_Swap_Size	EQU 	11					; 11 bytes to swap on switch

STRUC	Sprite_Item

	Current_X				dw	0				; position of sprite on 
	Current_Y				dw	0				; logical screen
	Current_Frame			dw	0				; frame in animation frame list
	Current_Background		dw	0ffffh			; pointer to saved background
	Current_Address 		dw	0				; pointer to address in vidram
	Current_Left_Plane		db	0				; side of sprite    

	Padding 				db	0				; let's keep it word aligned
    
	Previous_X				dw	0				; host address of top left
	Previous_Y				dw	0				; corner of sprite
	Previous_Frame			dw	0	 
	Previous_Background 	dw	0ffffh
	Previous_Address		dw	0		    
	Previous_Left_Plane 	db	0				; pointer to plane of left hand
    
	Inertia 				dw	0
    
	X_Velocity				db	0
	Y_Velocity				db	0
    
	Sprite_State			db	0				; state of sprite (flipped, clipped)
							dw	0
							dw	0


ENDS	Sprite_Item


GLOBAL	Sprite_List:Sprite_Item:Maximum_Sprites


;----------------------------------------------------------------------------:
; A frame of animation in the animation list								 :
;----------------------------------------------------------------------------:

STRUC	Frame_Item

	Image_Data			dd	?					; address of image of frame
	Image_X_Size		dw	?					; x and y dimensions
	Image_Y_Size		dw	?
	Image_Mask			dd	?					; address of count mask
	Image_Data_Size 	dw	?					; size of all image data
	Image_Plane_Size	dw	?					; size of 1 planes data in bytes
	    
ENDS	Frame_Item


GLOBAL Frame_List:Frame_Item:Maximum_Frames

;----------------------------------------------------------------------------:
; The header of a .PCX or .PCC file 										 :
;----------------------------------------------------------------------------:

STRUC	PCX_Header
	Id						db	?
	Version 				db	?
	Encoding_Mode			db	?
	Bits_per_Pixel			db	?
	X1						dw	?
	Y1						dw	?
	X2						dw	?
	Y2						dw	?
	Horizontal_Resolution	dw	?
	Vertical_Resolution 	dw	?
	Colour_Map				db	48	dup (?)
	Not_Used				db	?
	Number_Of_Planes		db	?
	Bits_per_Plane_Line 	dw	?
	Palette_Information 	dw	?
							dw	?
							dw	?
	Extra					db	54	dup (?)
ENDS	PCX_Header

;----------------------------------------------------------------------------:
; A Colour Look up Table, or Palette!										 :
;----------------------------------------------------------------------------:


STRUC	PCX_CLUT
	Colour_Information		db	3 dup (256 dup (?))
ENDS	PCX_CLUT


********* Cut here for "VGA.INC"-----------------------------------------------
;----------------------------------------------------------------------------:
;																			 :
; INCLUDE File for VGA constants and useful macros							 :
;----------------------------------------------------------------------------:


;----------------------------------------------------------------------------:
; Colour VGA register equates												 :
;----------------------------------------------------------------------------:


; I/O ports for accessing VGA

Miscellaneous_Output_Write_Port EQU 03c2h
Miscellaneous_Output_Read_Port	EQU 03cch
Input_Status_0_Port 			EQU 03c2h
Input_Status_1_Port 			EQU 03dah
Sequencer_Address_Port			EQU 03c4h
Sequencer_Data_Port 			EQU 03c5h
CRTC_Address_Port				EQU 03d4h
CRTC_Data_Port					EQU 03d5h
Graphics_Address_Port			EQU 03ceh
Graphics_Data_Port				EQU 03cfh
Attribute_Address_Write_Port	EQU 03c0h
Attribute_Address_Read_Port 	EQU 03c1h   
PEL_Address_Write_Mode_Port 	EQU 03c8h
PEL_Address_Read_Mode_Port		EQU 03c7h
PEL_Data_Port					EQU 03c9h
DAC_State_Port					EQU 03c7h
PEL_Mask_Port					EQU 03c6h


; Addresses of sequencer registers

Reset					EQU 0
Clocking_Mode			EQU 1
Map_Mask				EQU 2
Character_Map_Select	EQU 3
Memory_Mode 			EQU 4

; Addresses of CRTC registers

Horizontal_Total			EQU 0
Horizontal_Display_End		EQU 1
Start_Horizontal_Blanking	EQU 2
End_Horizontal_Blanking 	EQU 3
Start_Horizontal_Retrace	EQU 4
End_Horizontal_Retrace		EQU 5
Vertical_Total				EQU 6
Overflow					EQU 7
Preset_Row_Scan 			EQU 8
Max_Scan_Line				EQU 9
Cursor_Start				EQU 10
Cursor_End					EQU 11
Start_Address_High			EQU 12
Start_Address_Low			EQU 13
Cursor_Location_High		EQU 14
Cursor_Location_Low 		EQU 15
Vertical_Retrace_Start		EQU 16
Vertical_Retrace_End		EQU 17
Vertical_Display_End		EQU 18
;Offset 					EQU 19  ; ATCHUNG!! Name Conflict.
Underline_Location			EQU 20
Start_Vertical_Blanking 	EQU 21
End_Vertical_Blanking		EQU 22
Mode_Control				EQU 23
Line_Compare				EQU 24

; Addresses of Graphics registers

Set_Reset				EQU 0
Enable_Set_Reset		EQU 1
Colour_Compare			EQU 2
Data_Rotate 			EQU 3
Read_Map_Select 		EQU 4
Mode					EQU 5
Miscellaneous			EQU 6
Colour_Dont_Care		EQU 7
Bit_Mask				EQU 8

; Addresses of Attribute registers

Palette 					EQU 0
Attribute_Mode_Control		EQU 010h
Overscan_Colour 			EQU 011h
Colour_Plane_Enable 		EQU 012h
Horizontal_Pixel_Panning	EQU 013h
Colour_Select				EQU 014h

MACRO	Wait_for_Vertical_Retrace
		LOCAL	VR_Wait
		PUSH	AX
		PUSH	DX
VR_Wait:
		MOV 	DX,Input_Status_1_Port			; hang around for vblank tick
		IN		AL,DX
		TEST	AL,8
		JZ		SHORT VR_Wait
		POP 	DX
		POP 	AX
ENDM

;----------------------------------------------------------------------------:
; Screen Resolution 														 :
;----------------------------------------------------------------------------:

VGA_X_Resolution EQU 320
VGA_Y_Resolution EQU 200

************** Cut here for SPRITES.ASM --------------------------------------
;----------------------------------------------------------------------------:
; Sprites.ASM           													 :
; (C) John Connors														1993 :
;----------------------------------------------------------------------------:

		IDEAL
		P386N	    

		INCLUDE "GLOBALS.INC"			    
		INCLUDE "STRUCTS.INC"	    
		INCLUDE "VGA.INC"

	    
;-----------------------------------------------------------------------------:
;								MACROS										  :
;-----------------------------------------------------------------------------:

;----------------------------------------------------------------------------:
; With X in BX and y in CX compute video address, into EDI, mask into AL	 :
; CX, BX preserved EDI, AH destroyed.										 :
; * This computes the video address on the currently nondisplayed screen	 :
;----------------------------------------------------------------------------:

; Logical_Scan_Start_Address holds the offset of the page to plot
; the sprite on.. it will be undefined when you assemble.
; You need to change it to whatever *your* double buffering code 
; uses.

MACRO	Compute_Video_Address
		ADD 	BX,[SMALL Left_Hand_Bit_Plane]
		MOV 	AX,CX							; width of display in bytes
		SHL 	AX,4
		MOV 	CX,AX
		SHL 	AX,2
		ADD 	AX,CX							; multiplied by y
		MOV 	CX,BX							; duplicate x for mask
		SHR 	BX,2							; shift out bits used for mask
		MOV 	DI,[SMALL WORD PTR Logical_Scan_Start_Address]
		ADD 	DI,AX							; add x and y contributions 
		ADD 	DI,BX
		MOV 	AL,CL							; AL is low bits of X
		AND 	AX,3							; mask is bottom two bits
ENDM    

;----------------------------------------------------------------------------:
;			DATA SEGMENT													 :
;----------------------------------------------------------------------------:


SEGMENT Data_Segment DWORD PUBLIC USE32 'DATA'
		INCLUDE "DATA.INC"
    
		ALIGN	4
	    
;----------------------------------------------------------------------------:
; buffer to hold sprite image data											 :
;----------------------------------------------------------------------------: 

Bottom_of_Image_Buffer		dd	0

;----------------------------------------------------------------------------:
; list of sprites															 :
;----------------------------------------------------------------------------:

		ALIGN	4

Sprite_List 			Sprite_Item Maximum_Sprites DUP (<>)
Sprite_Item_Size			= SIZE Sprite_List

;----------------------------------------------------------------------------:
; list of frames for animation												 :
;----------------------------------------------------------------------------:

		ALIGN	4

Frame_List					Frame_Item Maximum_Frames DUP (<>)
Frame_Item_Size 			= SIZE Frame_List


Map_Mask_Lookup 			db 01,02,04,08		; translate plane # into mask

ENDS	Data_Segment

;----------------------------------------------------------------------------:
;		IMAGE SEGMENT (Segment to hold image data for sprite frames 		 :
;----------------------------------------------------------------------------:

SEGMENT 		Image_Segment PARA PUBLIC USE32 'DATA'

				ALIGN	4

Image_Buffer	db 64000 dup (?)				; a lot of bytes!
				db 64000 dup (?)

ENDS			Image_Segment

;----------------------------------------------------------------------------:
; Transfer image data of sprites current frame to screen					 :
; Performs X wrapping only													 :
; Sprite colour 0 is treated as transparent 								 :
; Enter with sprite number to display in EAX						         :
;                                                                            :
; This code is part of a larger game and I have not time to re-write it so   :
; that it works with normal mode x sprites                                   :
; EAX is an index into the Sprite list, which holds X Y co-ords of sprites   :
; and things like that. The sprite list also contains an index into the      :
; Image list which holds the raw binary data for sprite images               :
; The Image is read from a .PCX file and laid out plane by plane             :
;                                                                            :
; Plane #1 Row #1, Row #2, Row #3...                                         :
; Plane #2 Row #1, Row #2, Row #3...                                         :
;                                                                            :
; The planes of the image are assumed all to be the size of the largest plane:
; in the image (so if only plane one has an extra pixel, the others will     :
; be padded out)                                                             :
;																		     :
; The code is also far from optimal. For a start neither source or           :
; or destination of the image transfer to VGA ram is word aligned            :
; .. and they *pay* me for this..sheesh!                                     :
;----------------------------------------------------------------------------:

PROC	Display_Wrapped_Sprite NEAR
		LOCAL	Wrapped_Width:WORD,UnWrapped_Width:WORD
		LOCAL	Wrap_Flag:BYTE,X_Pixel:WORD,Plane_Size:WORD
		LOCAL	Source_Plane:BYTE,Sprite_State:BYTE
		LOCAL	Plane_Number:WORD,Plane_Width:WORD
		LOCAL	Image_Width:WORD,Image_Height:WORD,Destination_Start:WORD
		LOCAL	Source_Start:WORD =Local_Length

		ENTER	Local_Length,0

		PUSHAD									; save registers
		PUSH	DS
	    
		ASSUME	DS:Data_Segment
	    
		MOV 	DX,Video_Segment
		MOV 	ES,DX							; ES points to VGA's segment
		ASSUME	ES:Video_Segment
	    
		ERRIF	(Sprite_Item_Size NE 20h)		; form address of sprite in SI
		MOV 	SI,AX
		SHL 	SI,5
		ADD 	SI,OFFSET Sprite_List
												; get state of sprite
		MOV 	AL,[(Sprite_Item PTR SI).Sprite_State]
		MOV 	[Sprite_State],AL		    
												; get sprite x y
		MOV 	BX,[(Sprite_Item PTR SI).Current_X]
		MOV 	CX,[(Sprite_Item PTR SI).Current_Y]
		MOV 	[X_Pixel],BX					; save for x wrapping
		CMP 	BX,VGA_X_Resolution 			; trivially reject (off right)
		JGE 	NEAR @@Done_Last_Pixel
		Compute_Video_Address
		MOV 	[Destination_Start],DI			; video address
		MOV 	[Plane_Number],AX				; first screen plane of sprite 
												; form frame address in DI
		MOV 	DI,[(Sprite_Item PTR SI).Current_Frame]
		SHL 	DI,4
		ADD 	DI,OFFSET Frame_List
		MOV 	AX,[(Frame_Item PTR DI).Image_Plane_Size]
		MOV 	[Plane_Size],AX 		    
												; get sprite dimensions etc.
		MOV 	BX,[(Frame_Item PTR DI).Image_X_Size]
		MOV 	[Image_Width],BX  
		MOV 	AX,BX							; get image width in acc
		ADD 	AX,[X_Pixel]
		CMP 	AX,VGA_X_Resolution
		SETGE	[Wrap_Flag]                     ; do we need to wrap?
		MOV 	AX,BX							; work out absolute width of
		AND 	AX,NOT 3						; a source plane (width of
		CMP 	AX,BX							; widest plane in source)
		JZ		SHORT @@No_Plane_Adjust
		ADD 	AX,4
@@No_Plane_Adjust:
		SHR 	AX,2  
		MOV 	[Plane_Width],AX
		MOV 	CX,[(Frame_Item PTR DI).Image_Y_Size]
		MOV 	[Image_Height],CX
												; get address of sprite image
		MOV 	ESI,[(Frame_Item PTR DI).Image_Data] 
		MOV 	[Source_Start],SI				; SI is smallest possible offset

		SHR 	ESI,16	 
		MOV 	AX,SI							; get address to get image from
		MOV 	DS,AX							; in DS:SI (sauce)
		ASSUME	DS:NOTHING						; where?

		MOV 	CX,4							; four planes
@@Next_Plane:
		PUSH	CX
		; for each plane
		 ; set VGA write plane
		MOV 	SI,[Plane_Number]
		MOV 	AH,[BYTE CS:@@Map_Mask_Lookup+SI] 
		MOV 	AL,Map_Mask
		MOV 	DX,Sequencer_Address_Port
		OUT 	DX,AX

		 ; work out physical plane width (ie width of plane as actually 
		 ; displayed without padding)

		MOV 	BX,[Image_Width]
		ADD 	BX,CX
		DEC 	BX								; cx is plane count
		SHR 	BX,2							; bx is now plane width
		MOV 	[UnWrapped_Width],BX			; assume no wrapping
		MOV 	AL,[Wrap_Flag]
		OR		AL,AL                           ; check for wrapping?
		JZ		SHORT @@Wrapping_Done			    
@@X_Wrap:	   
		MOV 	AX,[UnWrapped_Width]			; yes, we have to adjust
		MOV 	BX,VGA_X_Resolution-1           ; sprite width
		SUB 	BX,[X_Pixel]
		SHR 	BX,2    
		INC 	BX
		MOV 	[UnWrapped_Width],BX  			; this is the new width
		SUB 	AX,BX
		MOV 	[Wrapped_Width],AX				; this is the width of 
	    										; the bit on the other side
												; of the screen
@@Wrapping_Done:	    
		MOV 	CX,[Image_Height]
		MOVZX	ESI,[Source_Start]
@@No_Source_Adjust:
		MOV 	DI,[Destination_Start]
		MOV 	DX,320/4						; screen row length
@@Next_Row:     
		 ; for each row
		PUSH	CX
		PUSH	DI
		PUSH	SI
		MOV 	BX,2
	    
		MOV 	CX,[UnWrapped_Width]
		SHR 	CX,1
		JZ		SHORT @@Words_Done

		ALIGN	4								; optimise loop jump
@@Next_Word:
		LODSW
		OR		AX,AX
		JZ		SHORT @@Transparent_Word		; most pixels in my
											    ; sprites are transparent
												; so I check for this first
		OR		AL,AL
		JZ		SHORT @@Transparent0
		MOV 	[ES:DI],AL
@@Transparent0:
		OR		AH,AH
		JZ		SHORT @@Transparent1
		MOV 	[ES:DI+1],AH
@@Transparent1:
@@Transparent_Word:
		ADD 	DI,BX
		LOOP	SHORT @@Next_Word
@@Words_Done:
		MOV 	CX,[UnWrapped_Width]
		RCR 	CX,1
		JNC 	SHORT @@Do_Wrapped_Side
		 ; copy <physical plane width bytes>
		LODSB
		OR		AL,AL
		JZ		SHORT @@Transparent
		MOV 	[ES:DI],AL
@@Transparent:
		INC 	DI
@@Do_Wrapped_Side:	    
		MOV 	AL,[Wrap_Flag]			; any wrapping ?
		OR		AL,AL
		JZ		SHORT @@Finished_Row   ; no - forget it
		SUB 	DI,DX

;
; Blit the part of the sprite that has been wrapped (if neccessary)
;
		MOV 	CX,[Wrapped_Width]
		SHR 	CX,1
		JZ		SHORT @@WWords_Done

		ALIGN	4
@@WNext_Word:
		LODSW
		OR		AX,AX
		JZ		SHORT @@WTransparent_Word
		OR		AL,AL
		JZ		SHORT @@WTransparent0
		MOV 	[ES:DI],AL
@@WTransparent0:
		OR		AH,AH
		JZ		SHORT @@WTransparent1
		MOV 	[ES:DI+1],AH
@@WTransparent1:
@@WTransparent_Word:
		ADD 	DI,BX
		LOOP	SHORT @@WNext_Word
@@WWords_Done:
		MOV 	CX,[Wrapped_Width]
		RCR 	CX,1
		JNC 	SHORT @@Finished_Row
		 ; copy <wrapped length width bytes>
		LODSB
		OR		AL,AL
		JZ		SHORT @@WTransparent
		MOV 	[ES:DI],AL
@@WTransparent:

@@Finished_Row:
		 ; next row
		 ; bump sauce
		POP 	SI
		ADD 	SI,[Plane_Width]
		 ; bump destination
		POP 	DI
		ADD 	DI,DX
		POP 	CX
		LOOP	@@Next_Row
@@Plane_Clipped_Out:
		; next plane
		; bump sauce
		MOV 	AX,[Plane_Size]
		ADD 	[Source_Start],AX
		; reset destination
		; bump	plane
		INC 	[Plane_Number]           		; bump onto next physical
		AND 	[Plane_Number],3                ; plane
		JNZ 	SHORT @@No_Physical_Plane_Adjust
		INC 	[Destination_Start]
@@No_Physical_Plane_Adjust:
		POP 	CX								; get plane count
		MOV 	AX,[X_Pixel]					; X pixel
		INC 	AX
		CMP 	AX,VGA_X_Resolution
		JL		SHORT @@No_Wrap_Check			; finish if off screen
		XOR 	BL,BL
		MOV 	[Wrap_Flag],BL					; turn off wrapping
		MOV 	BX,320/4                        ; if whole plane is wrapped
		SUB 	[Destination_Start],BX          ; and bump up a screen line
		XOR 	AX,AX
@@No_Wrap_Check:
		MOV 	[X_Pixel],AX
		DEC 	CX
		JNZ 	NEAR @@Next_Plane
@@Done_Last_Pixel:

		POP 	DS								; restore registers
		POPAD
	    
		LEAVE
	    
		RET     
@@Map_Mask_Lookup:
		db	01
		db	02
		db	04
		db	08
ENDP	Display_Wrapped_Sprite


;----------------------------------------------------------------------------:
; Transfer image data of sprites current frame to screen					 :
; Sprite colour 0 is *not* treated as transparent							 :
; Enter with EAX		   as sprite number to display						 :
;----------------------------------------------------------------------------:

; you don't actually need this, but ....
; i thought t'would be handy!

PROC	Display_Unclipped_Block NEAR
		LOCAL	Plane_Size:WORD,Plane_Number:WORD,Physical_Plane_Width:WORD
		LOCAL	Plane_Width:WORD,Bytes_In_Plane:WORD,Image_Width:WORD
		LOCAL	Image_Height:WORD,Destination_Start:WORD
		LOCAL	Source_Start:WORD =Local_Length

		ENTER	Local_Length,0

		PUSHAD									; save registers
		PUSH	DS
	    
		ASSUME	DS:Data_Segment
	    
		MOV 	DX,Video_Segment
		MOV 	ES,DX							; ES points to VGA's segment
		ASSUME	ES:Video_Segment

		ERRIF	(Sprite_Item_Size NE 20h)		; form address of sprite in SI
		MOV 	SI,AX
		SHL 	SI,5
		ADD 	SI,OFFSET Sprite_List
												; form video destination addr 
		MOV 	BX,[(Sprite_Item PTR SI).Current_X]
		MOV 	CX,[(Sprite_Item PTR SI).Current_Y]
		Compute_Video_Address
		MOV 	[Destination_Start],DI
												; get first plane of sprite 
		MOV 	[Plane_Number],AX
												; form frame address in DI
		MOV 	DI,[(Sprite_Item PTR SI).Current_Frame]
		SHL 	DI,4
		ADD 	DI,OFFSET Frame_List
		MOV 	AX,[(Frame_Item PTR DI).Image_Plane_Size]
		MOV 	[Plane_Size],AX     
												; get sprite dimensions etc.
		MOV 	BX,[(Frame_Item PTR DI).Image_X_Size]
		MOV 	[Image_Width],BX   
		MOV 	AX,BX
		AND 	AX,NOT 3
		CMP 	AX,BX
		JZ		SHORT @@No_Plane_Adjust
		ADD 	AX,4
@@No_Plane_Adjust:
		SHR 	AX,2  
		MOV 	[Plane_Width],AX
		MOV 	CX,[(Frame_Item PTR DI).Image_Y_Size]
		MOV 	[Image_Height],CX
												; get address of sprite image
		MOV 	ESI,[(Frame_Item PTR DI).Image_Data] 
		MOV 	[Source_Start],SI				; SI is smallest possible offset

		SHR 	ESI,16							; high word of ESI is segment
		MOV 	AX,SI
		MOV 	DS,AX							; in DS:SI (sauce)
		ASSUME	DS:NOTHING						; where?
		MOV 	CX,4							; four planes
@@Next_Plane:
		PUSH	CX
		; for each plane
		 ; set write plane
		MOV 	SI,[Plane_Number]				; convert plane # to mask
		MOV 	AH,[BYTE CS:@@Map_Mask_Lookup+SI] 
		MOV 	AL,Map_Mask
		MOV 	DX,Sequencer_Address_Port
		OUT 	DX,AX							; send to hardware
		 ; work out physical plane width
		MOV 	BX,[Image_Width]
		ADD 	BX,CX							; cx is plane count
		DEC 	BX
		SHR 	BX,2							; bx is now plane width
		MOV 	[Physical_Plane_Width],BX
		MOVZX	ECX,[Image_Height]
		MOV 	SI,[Source_Start]
		MOV 	DI,[Destination_Start]
		MOV 	DX,320/4						; screen row length
		SUB 	DX,[Physical_Plane_Width]
	    
		ALIGN	4
@@Next_Row:     
		 ; for each row
		PUSH	BX	    
		PUSH	CX
		PUSH	SI

	    
		MOV 	CX,BX							; bx == [Physical_Plane_Width]
		 ; copy <physical plane width bytes>
		 
		TEST	DI,3
		JZ		SHORT @@Dword_Aligned
		MOV 	CX,DI
		AND 	CX,3
		SUB 	BX,CX	    
@@Align_Data:
		LODSB
		OR		AL,AL
		JZ		SHORT @@Lead_Transparent
		MOV 	[ES:DI],AL
@@Lead_Transparent:
		INC 	DI
		LOOP	SHORT @@Align_Data

@@Dword_Aligned:

		MOVZX	ECX,BX
		SHR 	ECX,2
		REP 	MOVSD
		MOV 	CX,BX							; bx == [Physical_Plane_Width]
		AND 	CX,3
		REP 	MOVSB					    
		 ; next row
		 ; bump sauce
		POP 	SI
		ADD 	SI,[Plane_Width]
		 ; bump destination
		ADD 	DI,DX							; DX == 320/4
		POP 	CX
		POP 	BX
		LOOP	SHORT @@Next_Row
		; next plane
		; bump sauce
		MOV 	AX,[Plane_Size]
		ADD 	[Source_Start],AX
		; reset destination
		; bump	plane
		INC 	[Plane_Number]
		AND 	[Plane_Number],3
		JNZ 	SHORT @@No_Physical_Plane_Adjust
		INC 	[Destination_Start]
@@No_Physical_Plane_Adjust:
		POP 	CX
		LOOP	SHORT @@Next_Plane
			    
		POP 	DS								; restore registers
		POPAD
	    
		LEAVE

		RET     
@@Map_Mask_Lookup:
		db	01
		db	02
		db	04
		db	08
ENDP	Display_Unclipped_Block

;----------------------------------------------------------------------------:
; Limit Sprite : Limits sprite to screen borders							 :
; Enter with EAX as sprite number											 :
;----------------------------------------------------------------------------:

PROC	Limit_Sprite NEAR
		PUSHA
		ASSUME	DS:Data_Segment
		SHL 	AX,5							; from sprite address
		MOV 	SI,AX
		ADD 	SI,OFFSET Sprite_List
		MOV 	DI,[(Sprite_Item PTR SI).Current_Frame]
		SHL 	DI,4							; form frame address
		ADD 	DI,OFFSET Frame_List
												; from right bottom sprite 
												; co-ordinate
		MOV 	BX,[(Frame_Item PTR DI).Image_X_Size]	  
		MOV 	CX,[(Frame_Item PTR DI).Image_Y_Size]
		ADD 	BX,[(Sprite_Item PTR SI).Current_X]
		ADD 	CX,[(Sprite_Item PTR SI).Current_Y]
		CMP 	BX,VGA_X_Resolution
		JBE 	SHORT @@No_X_Clip
		MOV 	BX,VGA_X_Resolution 		; form clipped x 
		SUB 	BX,[(Frame_Item PTR DI).Image_X_Size]
		MOV 	[(Sprite_Item PTR SI).Current_X],BX
@@No_X_Clip:
		CMP 	CX,VGA_Y_Resolution     
		JBE 	SHORT @@No_Y_Clip
		MOV 	CX,VGA_Y_Resolution 		; form clipped y
		SUB 	CX,[(Frame_Item PTR DI).Image_Y_Size]
		MOV 	[(Sprite_Item PTR SI).Current_Y],CX
@@No_Y_Clip:
		POPA
		RET
ENDP	Limit_Sprite

ENDS	Code_Segment


	END