home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
assemblr
/
library
/
sampler0
/
sprites2.asm
< prev
next >
Wrap
Assembly Source File
|
1986-05-27
|
21KB
|
615 lines
;
; *** Listing 2 ***
;
;These routines produce the effect of hardware sprites in software
; on IBM PC compatible computers. They put objects onto the screen
; in a manner which preserves the background, and produces no
; undesirable fringe or overlap effects. Operations which affect
; video buffer memory are performed as much as possible during
; video non-display periods to avoid other undesirable effects.
;
; Entry points and parameters:
;
; Initialize - Sets the background buffer address to be used to erase
; objects, resets internal flags and queue, and on EGAs
; sets up the use of the vertical interrupt to drive the
; drawing routines.
;
; Inputs - AX holds paragraph address of background buffer.
; Outputs - None.
;
; Terminate - Resets the EGA vertical interrupt hardware and vector
;
; Inputs - None.
; Outputs - None.
;
; Object_services - Sets X,Y and Form address for a given object
; to be drawn, and activates or deactivates the object.
;
; Inputs - CX holds X position in bytes (0-79) of upper
; left hand corner of object. 0 is leftmost.
; - BX holds Y position in lines (0-198) 0 is top.
; BX must be even! Objects cannot start on odd lines.
; Objects must also be an even number of lines high.
; - DI holds object number. Higher numbered objects
; will appear to be in front of lower numbered
; objects when they overlap.
; - SI holds the offset in the code segment of the form
; to be drawn for the object. A value of 0ffffh means
; that the object is to be erased, then ignored.
; Forms must be in the following format:
;
; byte 0 - height in lines (h)
; byte 1 - width in bytes (w)
; followed by w X h (mask word, image word) pairs.
;
; Outputs - None.
;
; Registers - All are saved, except flags
;
; Warning - No bounds checking is done. X,Y or object numbers
; out of range can send your program into hyperspace.
;
; Put_objects_on_screen - This routine should be called by a program
; running on a CGA to put the objects on the screen. It must be
; far-called as if it were an interrupt routine. For example:
;
; pushf
; call far ptr put_objects_on_screen
;
; For best results this routine should be called immediately
; upon the sensing of vertical retrace.
;
; Inputs - None.
; Outputs - None.
;
; Vert_int_modulo_count - This memory word is incremented each time
; the objects are put into the screen map. On EGAs it can be used
; to synchronize a program to the constant time base provided by
; the vertical interrupt.
;
;The flag below must be set properly before assembling this program
;
ega equ 0 ;1 to assemble for Enhanced Graphics Adapter
;0 to assemble for Color Graphics Adapter
cga equ (ega xor 1) ;the opposite status of ega
;
bios_data_segment segment at 40h ;BIOS keeps its data at 400h;
org 63h ;at 463h is a word that holds
bios_crtc_base_address dw ? ; the CRT controller's base
bios_data_segment ends ; address
;
;
cseg segment para public 'cseg'
assume cs:cseg,ds:cseg,es:nothing
public initialize,terminate,object_services
public put_objects_on_screen,vert_int_modulo_count
;
;Memory for the parameters used to keep track of objects is reserved
; below. Many of the parameters stored are very code specific so that
; the size and number of objects which could be processed during
; vertical non-display time could be maximized.
;
number_of_objects equ 3 ;this should be set to the maximum number
; of objects or priorities which will
; need to be kept track of at one time.
;
queue label word
;
draw_screen_offset dw ? ;offset in screen memory buffer of upper
; left hand corner of object. 0ffffh if
; object is to be ignored.
dist_to_odd_scan_line dw ? ;distance from end of object on an even
; scan line to the start of the object
; on the next (odd) scan line
dist_to_even_scan_line dw ? ;distance from end of object on an odd
; scan line to the start of the object
; on the next (even) scan line
;
erase_parms label word
;
erase_width dw ? ;the object's screen image width in words
erase_entry_point dw ? ;the address of the inline code to do erase
erase_screen_offset dw ? ;the address where object was last drawn
; 0ffffh if object is not to be erased.
erase_image_offset dw ? ;used to determine if need to erase when
; object is in old position
;
length_of_erase_parms equ $-erase_parms
;
draw_col_entry_point dw ? ;address of the column code for drawing
draw_row_entry_point dw ? ;address of the row inline code for drawing
draw_image_offset dw ? ;offset in the code segment of the image
;
queue_item_length equ ($ - queue) ;number of bytes for each item
distance_from_entry_point_to_next_item equ $ - erase_entry_point
distance_from_image_to_next_item equ $ - draw_image_offset
;
db ( (number_of_objects-1) * queue_item_length ) dup(?)
end_of_queue label word
;
vert_int_modulo_count dw 0 ;incremented each time a vertical
; interrupt occurs
background_segment dw ? ;place to hold the paragraph address
; of the background buffer used to
; erase objects
crtc_base_address dw ? ;will hold register address
;
old_int10_offset dw ? ;place to store the vector contents
old_int10_segment dw ? ; so they can be restored when finished
;
old_int_mask db ? ;place to store the mask register's
; contents so it can be restored
;
true equ 1 ;used for flag values
false equ 0 ;
;
need_to_draw_something_flag db false ;true if a change needs to be
; made to any of the objects'
; screen images
;
screen_buffer_paragraph_adr equ 0b800h
;
;
initialize proc near
cld ;count up
push ds ;
mov cs:[background_segment],ax ;store background adr
mov ax,cs ;make data segment
mov ds,ax ; same as code segment
; since that is where data
mov es,ax ; used by this routine is
mov di,offset queue ;turn off all objects
mov cx,(number_of_objects * queue_item_length)/2
mov ax,0ffffh ;
rep stosw ;
;
mov [need_to_draw_something_flag],false ;nothing to draw
if ega
sub ax,ax ;swapping interrupt
mov ds,ax ; vectors with our
mov bx,(10*4) ; interrupt handler
mov ax,offset put_objects_on_screen ;our vertical int
mov dx,cs ; handler address
cli ;disable interrupts
xchg [bx],ax ;offset
xchg [bx+2],dx ;segment
mov cs:[old_int10_offset],ax ;save old value so we
mov cs:[old_int10_segment],dx ; can restore it upon
; ; termination
mov ax,bios_data_segment ;find the register
mov ds,ax ; address
assume ds:bios_data_segment ;
mov dx,[bios_crtc_base_address] ;
mov cs:[crtc_base_address],dx ;save it in code seg
mov al,11h ;select vertical
out dx,al ; retrace end register
mov al,04h ; and flip it off
inc dx ;
out dx,al ;
mov al,14h ; then flip it on
out dx,al ;
;
in al,21h ;enable IRQ2
mov cs:[old_int_mask],al ; save old value
and al,not 4 ;
out 21h,al ;
;
sti ;enable interrupts
endif
pop ds ;restore data segment
ret
initialize endp
;
terminate proc near ;only needs to be used when assembled
if ega ; for use on an EGA
mov dx,[crtc_base_address]
mov al,11h
out dx,al
inc dx
mov al,24h ;bit 5 high to disable, bit 4 low to
out dx,al ; clear vertical interrupt
push ds
sub ax,ax ;restore original interrupt
mov ds,ax ; 10 vector
mov bx,(10*4) ;
mov ax,cs:[old_int10_offset] ;
mov dx,cs:[old_int10_segment] ;
cli ;make sure interrupt
mov [bx],ax ; doesn't occur while
mov [bx+2],dx ; there is an inconsistant
; vector/mask
mov bl,cs:[old_int_mask] ;restore IRQ2 mask bit
and bl,4 ; to state it had when
in al,21h ; Initialize was called
and al,not 4 ;
or al,bl ;
out 21h,al ;
sti
pop ds
endif
ret
terminate endp
;
object_services proc near
cld ;
push es ;save the registers used
push ds ;
push ax ;
push bx ;
push cx ;
push si ;
push di ;
;
mov ax,cs ;everything will be in code segment
mov es,ax ;
mov ds,ax ;
;
shl di,1 ;multiply object number
shl di,1 ; which is in DI by 20 to
mov ax,di ; find object's parameter table
shl di,1 ; offset in queue structure
shl di,1 ; (NOTE: If a code change alter
add di,ax ; queue_item_length this code must
; ; be changed!)
mov ax,offset queue ;point directly to object's first
add di,ax ; parameter
mov ax,[bx+even_line_screen_offset_table]
add ax,cx ;find screen offset of top left corner
if ega
cli ;can't allow parameters to be just half
; changed if a vertical interrupt occurs
endif
cmp si,0ffffh ;if object is to be turned off then
jne save_position ; need to store a 0ffffh for the draw
mov ax,si ; screen position
stosw ;
jmp short finish_services
save_position: ;
stosw ;save as first parameter (draw_screen_offset)
lodsb ;get the height of the image
xor ah,ah ;make height a word
mov bx,ax ;store height
lodsb ;get the width of the image in bytes
mov cx,2000h ;calculate amount to add after even scan
sub cx,ax ; lines are drawn to get the address of the
xchg ax,cx ; next scan line, and store it in queue
stosw ;
mov ax,1fb0h ;calculate amount to subtract after odd scan
add ax,cx ; lines are drawn to get the address of the
stosw ; next scan line, and store it in queue
mov ax,cx ;store the width in queue
shr ax,1 ; width is stored as number of words
stosw ;
mov ax,[bx+erase_inline_vector_table-2]
;-2 because there is no 0 lines entry point
stosw ;store the place to jump to erase an image
; of this height
add di,4 ;skip erase_screen_offset and
; erase_image_offset as these are filled
; in when an object is drawn
xchg si,cx ;swap image offset with width
mov ax,[si+column_inline_vector_table-2] ;inline code adr
stosw ; driver operates with words, so there is no
; need to divide SI by two to do table lookup
mov ax,[bx+row_inline_vector_table-2] ;inline code adr
stosw ; which calls column inline code for each row
mov [di],cx ;last param to put on queue is image offset
;
finish_services:
mov [need_to_draw_something_flag],true ;record change
if ega
sti ;all parameters have been put on queue
; so interrupts are safe now
endif
pop di ;restore those registers that were used
pop si ;
pop cx ;
pop bx ;
pop ax ;
pop ds ;
pop es ;
ret ;
object_services endp
;
;This table is used to find the offset of an even scan line in the
; memory map of the color graphics adapter in medium resolution mode.
;
even_line_screen_offset_table label word
xx=0
rept 100 ;there are 100 even lines
dw xx*50h ; each is 50h (80 decimal) long
xx=xx+1
endm
;
use_old_vector:
pop ax ;restore registers used before
pop dx ; jumping to previous IRQ2 handler
jmp dword ptr cs:[old_int10_offset]
;
put_objects_on_screen proc far
push dx ;save registers used by EGA code
push ax ;
if ega
;must check if interrupt is being signaled
; by the EGA card. If not, it needs to be
mov dx,3c2h ; handled by another routine in the vector
in al,dx ; chain. The PC AT in particular uses
test al,80h ; IRQ2 for multiple devices.
jz use_old_vector
endif
inc cs:[vert_int_modulo_count] ;count vert interrupts
sti ;enable interrupts
cmp cs:[need_to_draw_something_flag],true ;anything to do?
je process_queue ; jmp if there is
; otherwise do nothing
if ega
cli
mov al,20h ;issue a non_specific EOI (End Of Interrupt)
out 20h,al ; so that interrupt controller chip will
; acknowledge future vertical interrupts
mov dx,cs:[crtc_base_address] ;re-enable ega card interrupt
mov al,11h ; select vertical retrace end
out dx,al ; register and clear vertical interrupt
mov al,04h ;
inc dx ;
out dx,al ;
mov al,14h ; then enable vertical interrupt
out dx,al ;
endif
pop ax ;restore original values
pop dx ;
iret
;
skip_this_object:
add si,queue_item_length ;point SI to next item
cmp si,offset end_of_queue ;see if we are done
je draw ; jmp to draw if we are
jmp get_next_objects_screen_adr ; if not, erase next
;
process_queue:
push bx ;save the rest of the world
push cx ;
push bp ;
push si ;
push di ;
push ds ;
push es ;
;
push cs ;setup environment
pop ds ; data is in code segment
cld ; we will count up
mov ax,screen_buffer_paragraph_adr
mov es,ax ; ES points to screen memory
;
mov si,offset queue ;point to beginning of queue
;
;Erase all the active objects which have old screen positions
; different from their present screen position or have different
; image offsets
;
get_next_objects_screen_adr:
mov ax,[si+erase_screen_offset-draw_screen_offset]
; get the screen buffer
; offset of last draw
cmp ax,0ffffh ;if ffffh then object is
je skip_this_object ; yet to be drawn
cmp ax,[si] ;if new and old positions and
jne erase_this_object ; images are same then skip erase
mov di,[si+erase_image_offset-draw_screen_offset]
cmp di,[si+draw_image_offset-draw_screen_offset]
je skip_this_object ;
erase_this_object:
inc si ;point to next parameter
inc si ;
mov di,ax ;save screen buffer adr
lodsw ;get distance to odd scan line
mov bp,ax ; save in BP for inline code use
lodsw ;get distance to even scan line
mov dx,ax ; save in DX for inline code use
lodsw ;get the width in words for erase
mov cx,[si] ;get address of erase inline code
;
push si ;save position so next object
push ds ; can be found
mov ds,[background_segment] ;stuff to erase with
call cx ;erase it!
pop ds ;restore where
pop si ; we left off in queue
add si,distance_from_entry_point_to_next_item ;next item
cmp si,offset end_of_queue ;see if we are done
jne get_next_objects_screen_adr ; jmp if more to erase
;
;Draw all the active objects by AND/ORing into screen buffer
;
draw:
mov si,offset queue ;point to beginning of queue
;
get_next_objects_screen_adr2:
lodsw ;get screen buffer offset
mov [si+erase_screen_offset-draw_screen_offset-2],ax
;save what will be old position
mov di,[si+draw_image_offset-draw_screen_offset-2]
;save what will be old image offset
mov [si+erase_image_offset-draw_screen_offset-2],di
cmp ax,0ffffh ;see if object is active
je skip_this_object2 ; jmp if it isn't
mov di,ax ;save screen buffer adr
lodsw ;get distance to odd scan line
mov bp,ax ; save in BP for inline code use
lodsw ;get distance to even scan line
mov dx,ax ; save in DX for inline code use
add si,length_of_erase_parms ;skip the erase parameters
lodsw ;get the draw inline row adr
mov cx,ax ; save in CX for inline use
lodsw ;get the draw inline row adr
mov bx,ax ; save in BX for call to inline
lodsw ;get draw_image offset
push si ;save pointer to next queue item
mov si,ax ;save draw image in SI
call bx ;draw it!
pop si ;restore where we left off in queue
cmp si,offset end_of_queue ;see if we are done
jne get_next_objects_screen_adr2 ; jmp if more to erase
;
finish_up:
if ega
cli
mov al,20h ;issue a non_specific EOI (End Of Interrupt)
out 20h,al ; so that interrupt controller chip will
; acknowledge future vertical interrupts
mov dx,cs:[crtc_base_address] ;re-enable interrupt
mov al,11h ; select vertical retrace end
out dx,al ; register and clear vertical interrupt
mov al,04h ;
inc dx ;
out dx,al ; then enable vertical interrupt
mov al,14h ;
out dx,al ;
endif
pop es ;restore all registers
pop ds ;
pop di ;
pop si ;
pop bp ;
pop cx ;
pop bx ;
pop ax ;
pop dx ;
mov cs:[need_to_draw_something_flag],false
;indicate no reason to draw again until the
; queue is changed
iret ;restore flags and contine where interrupted
;
skip_this_object2:
add si,(queue_item_length-2) ;point SI to next item
cmp si,offset end_of_queue ;see if we are done
jne get_next_objects_screen_adr2 ; jmp if not
jmp short finish_up ; jmp if all finished
put_objects_on_screen endp
;
;
;This is inline code for finding the screen address for each line
; of the image and calling the AND-OR inline code.
;
rlabel macro xx ;this macro is used to label the inline code
rline&xx&: ; entry points
endm
;
;
; inline code for rows
;
xx=42 ;there will be an entry point for each even
; number of lines between 2 and 40. They will
; be labeled "rline2", "rline4", ... "rline40"
rept 20 ;each repeat handles two lines
xx=xx-2 ;calculate number of lines for entry point
rlabel %xx ;put in label for entry point
call cx ;CX holds address of inline columns code
add di,bp ;calculate the address to start next line
call cx ;process image for odd scan line
sub di,dx ;calculate the address to start next line
endm ; the next line will be an even line
ret
;
;Inline code for AND-ORing a line of the image into the screen
;
clabel macro xx ;this macro is used to label the inline code
cline&xx&: ; entry points for number of columns to AND-OR
endm ;
;
xx=10 ;this code can handle an image up to ten words
rept 10 ; wide
clabel %xx ;put in label for entry based on number of
; words in a column
lodsw ;get mask word
and ax,es:[di] ;mask out background
or ax,[si] ;insert data word
inc si ;point to next mask word
inc si ;
stosw ;return modified word to memory
xx=xx-1 ;adjust label number
endm
ret ;this return is executed at the end of every
; line
;
;This table is used as an indirect address for jumping into
; the inline code for image moving.
;
row_inline_vector_table label word ;there is no entry point for zero
; lines. Starting at 2 eliminates
; the need to store a dummy entry
; point address
row_entry_address macro xx ;this macro is used to generate
dw rline&xx& ; the labels corresponding to the
endm ; inline code entry points
;
xx=2
rept 20
row_entry_address %xx
xx=xx+2
endm
;
;This table is used as an indirect address for jumping into
; the inline code for exclusive-ORing columns.
;
column_inline_vector_table label word ;there is no entry point for zero
; lines. Starting at 2 eliminates
; the need to store a dummy entry
; point address
column_entry_address macro xx ;this macro is used to generate
dw cline&xx& ; the labels corresponding to the
endm ; inline code entry points
;
xx=1
rept 10
column_entry_address %xx
xx=xx+1
endm
;
;This is inline code for erasing the image by restoring the screen
; memory map from the background buffer.
;
elabel macro xx ;this macro is used to label the inline code
eline&xx&: ; entry points
endm
;
xx=42 ;there will be an entry point for each even
; number of lines between 2 and 40. They will
; be labeled "eline2", "eline4", ... "eline40"
rept 20
xx=xx-2 ;calculate number of lines for this entry point
elabel %xx ;put in label for entry point
mov si,di ;erase using same offset in background buffer
mov cx,ax ;put width of image in words in CX to prepare for
rep movsw ; repeated move string on even line
add di,bp ;calculate address of next line DI + (2000h-width)
mov si,di ;erase using same offset in background buffer
mov cx,ax ;put width of image in bytes in CX to prepare for
rep movsw ; repeated move string on odd line
sub di,dx ;calculate address of next line DI - (1fb0h+width)
endm
ret
;
;This table is used as an indirect address for jumping into
; the inline code for erasing an image.
;
erase_inline_vector_table label word ;there is no entry point for zero
; lines. Starting at 2 eliminates
; the need to store a dummy entry
; point address
entry_address macro xx ;this macro is used to generate
dw eline&xx& ; the labels corresponding to the
endm ; inline code entry points
;
xx=2
rept 20
entry_address %xx
xx=xx+2
endm
;
cseg ends
end