_REAL-TIME ANIMATION_ by Rahner James [LISTING ONE] .model small, c .286 ; This directive can be used to optimize procedure entry ; but not much else. I avoided all non-8088 commands comment \ EGA Sprite Drivers for C Copyright (c) February 1989, Ryu Consulting, Inc. (916) 722 - 1939 anytime Written by Rahner James, CS This is a full functioning sprite driver for EGA graphics adaptors. The sprite are given to the routines as a linked list of sprite structures. All the function are re-entrant and can be part of a multi-tasking system. The sprite structures are intended to reside in far memory. The sprite can exist on any pixel boundary. These were intended to be called from some C program, but probably can be modified for some other language. This must be assembled with Microsoft MASM version 5.0 or later since I make use of local variables, forward/backward jumps and models. Expect to get some incorrect size warnings because MASM doesn't seem to recognize its own "byte ptr" and "word ptr" operators when used with words and dwords. \ ; **************************************************************************** ; EQUATES ; **************************************************************************** EOI equ 20h ; End Of Interrupt signal EOI_PORT equ 20h ; Port to output the EOI CRT_MODE equ 49h EGA_ADDRESS equ 63h EGA_PIXELS_WORD equ 16 ; Number of pixels per word EGA_PIXELS_BYTE equ 8 ; Number of pixels per byte NUMBER_OF_PLANES equ 4 ; Number of EGA color planes EGA_RETRACE_STATUS equ 3dah ; EGA retrace status register RETRACE_BIT equ 1 shl 3 ; Bit set to signal a vertical retrace SEQUENCE_REG equ 3c4h ; Sequencer register GRAPHICS_12 equ 3ceh ; Graphics 1 & 2 register MAP_MASK_REG equ 2 ; Map mask Indexed register DATA_ROTATE_REG equ 3 ; Data Rotate Indexed register DATA_OR equ 1 shl 4 ; Set to OR data on the EGA DATA_MOVE equ 0 ; Write data unmodified onto EGA BOTTOM_LINE equ 200 ; Lowest pixel line to allow a sprite RIGHT_SIDE equ 640 ; Right-most visual pixel allowed ; **************************************************************************** ; STRUCTURES ; **************************************************************************** ; All sprites are stored on disk using the same internal format. ; The first word is the width of the sprites in bytes and the second ; word is the height of the sprite in widths. Each byte represents ; one pixel's worth of information. Bit 7 of the byte is the intensity ; bit, 0=off. This allows for 128 colors, black will be 0 or 80h. The ; intensity bit set indicates an opaque black surface. All color ; translations are table driven. internal_sprite_structure struc ; Storage structure used for sprites int_width dw ? ; Width in bytes for the sprite int_height dw ? ; Height in widths of the sprite int_body db ? ; Start of the sprite's body internal_sprite_structure ends ega_sprite_structure struc ; Internal EGA sprite structure e_animate_ptr dw 0,0 ; Far ptr to next sprite struct in animation seq. e_width dw ? ; Width of the sprite in words e_height dw ? ; Height of sprite in widths e_body dw ? ; Beginning of body ;.word 0: mask, word 1: sprite ;.throughout the body. That way you can pull ;.the background up, mask it, OR the sprite, ;.then store the background ; The body is organized into four planes, ;.termed PLANE0 to PLANE3. Each represents a ;.different color in the EGA spectrum, except ;.PLANE0 which is the intensity bit. ega_sprite_structure ends style_structure struc style_width dw ? ; Width of each style entry in bytes style_height dw ? ; Height of each style entry in pixels style_body db ? ; Start of the style entries style_structure ends ; **************************************************************************** ; LOCAL DATA STORAGE for DS ; **************************************************************************** .data public do_page_flip, done_page_flip even do_page_flip db 0 ; Set to -1 when non-visual page is completed done_page_flip db -1 ; Set to -1 right after page has been swapped flip_turn db 4 ; # of interrupts before an EGA page flip old_irq_mask db 0 ; Old IRQ mask EGA_settings db 2bh,2bh,2bh,2bh,24h,24h,23h,2eh db 0,0,0,0,0,24h,23h,2eh,2bh even ega_base_port dw ? default_retrace label word v_retrace_reg db 11h v_retrace_value db ? .code ; **************************************************************************** ; LOCAL DATA STORAGE for CS ; **************************************************************************** even ega_segment dw 0a800h ; EGA page memory segment being set up old_vector dw 0,0 ; Old IRQ-2 vector (as Checkov would say wecter) ; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE ; EGA sprite related routines below this line ; void EGA_CONVERT( dest_ptr, source_ptr ) ; Converts long storage format sprite into EGA structure sprite ; Given: ; dest_ptr far -> EGA sprite buffer ready to go ; source_ptr far -> disk sprite structure to convert ; Returns: ; EGA sprite buffer set up accordingly ega_convert proc near uses di si ds, dest:dword, source:dword local store_width:word, store_height:word local source_width:word local plane_size:word cld lds si, source ; DS:SI -> source sprite les di, dest ; ES:DI -> CGA sprite buffer mov es:[di], di ; Make the animate ptr point to itself mov es:[di+2], es add di, E_WIDTH lodsw ; Get width in byte pixels mov source_width, ax add ax, EGA_PIXELS_WORD-1 ; Want to include those border pixels mov cl, 4 ; Divide by sixteen to convert one byte shr ax, cl ;.per pixel to 16 pixels/word for EGA mov bx, ax ; Use this as our width count stosw ; Store as words/pixels lodsw ; Get height mov store_height, ax stosw ; Move height straight across mul bx ; AX = Body size in mask/sprite entries add ax, ax ; AX = body size in words add ax, ax ; AX = plane size in bytes mov plane_size, ax ; Save as our plane index mov ax, ds ; Swap DS:SI and ES:DI mov bx, es mov ds, bx mov es, ax xchg di, si sub si, 4 ; This is to prep for the next INC next_row: mov cx, source_width ; Get source row width next_word: add si, 4 ; SI -> next word in line mov dx, -1 ; DX = the destination mask xor ax, ax mov [si], dx ; Set the mask word mov [si+2], ax ; Clear the sprite word mov bx, plane_size ; BX offset to next plane mov [si+bx], dx ; Set the mask word mov [si+bx+2], ax ; Clear the sprite word add bx, plane_size ; BX offset to next plane mov [si+bx], dx ; Set the mask word mov [si+bx+2], ax ; Clear the sprite word add bx, plane_size ; BX offset to next plane mov [si+bx], dx ; Set the mask word mov [si+bx+2], ax ; Clear the sprite word mov dx, 1 shl 7 ; Start at MSB which is pixel LSB next_pixel_byte: jc next_word ; Only be set by pixel shift below mov al, es:[di] ; Get the source pixel byte inc di ; DI -> next source pixel byte or al, al ; See if it's anything at all jz end_pixel_byte ; Skip all the checks xor [si], dx ; Reset the mask bit test al, 1 shl 4 ; Check bit 7 jz @F ; Skip if nothing here or [si+2], dx ; Place the sprite bit @@: mov bx, plane_size ; BX -> plane 1 offset xor [si+bx], dx ; Clear the mask bit test al, 1 shl 7 ; Check bit 6 jz @F ; Skip if nothing here or [si+bx+2], dx ; Place the sprite bit @@: add bx, plane_size ; BX -> plane 2 offset xor [si+bx], dx ; Clear the mask bit test al, 1 shl 6 ; Check bit 5 jz @F ; Skip if nothing here or [si+bx+2], dx ; Place the sprite bit @@: add bx, plane_size ; BX -> plane 3 offset xor [si+bx], dx ; Clear the mask bit test al, 1 shl 5 ; Check bit 4 jz end_pixel_byte ; Skip if nothing here or [si+bx+2], dx ; Place the sprite bit end_pixel_byte: shr dl, 1 ; Move pixel bit toward MS pixel bit rcr dh, 1 loop next_pixel_byte ; Loop through the pixel bytes dec store_height ; One less row jnz next_row ret ega_convert endp ; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE ; ui EGA_CALCULATE( source_ptr ) ; Calculates the amount of storage needed for an unconverted sprite ; Given: ; source_ptr far -> disk sprite structure to convert ; Returns: ; AX = number of bytes needed to store the converted sprite ega_calculate proc near uses si ds, source:dword lds si, source ; DS:SI -> disk sprite structure for calculation mov ax, [si] ; Get the width in bytes add ax, EGA_PIXELS_WORD-1 ; Round up to nearest word shr ax, 4 ; AX = number of words for stoarge add ax, ax ; AX = number of bytes storage add ax, ax ; AX = number of globs in one row add ax, ax ; AX = number of row/planes add ax, ax mul word ptr [si].int_height ; AX = bytes per row * number of rows add ax, E_BODY ; AX = body size + header size ret ega_calculate endp ; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE ; void PUT_SPRITE( ui X, ui Y, ega_sprite_structure far *SPRITE_PTR ) ; Maps a sprite list into the display video buffer ; Sprite is assumed to be 32-bits wide ; Given: ; X = X pixel location of sprite ; Y = Y pixel location of sprite ; SPRITE_PTR -> sprite structure to put on the screen ; Returns: ; The sprite is mapped onto the screen buffer put_sprite proc uses di si, x:word, y:word, sprite_ptr:dword local mask_msw:word ; OR'ed with the mask dword local mask_lsw:word local or_msw:word ; AND'ed with the sprite dword local or_lsw:word local start_di:word local start_si:word local sprite_plane_size:word local number_of_rows:word local shift:byte local plane_number:byte push ds cmp y, 200 ; See if below 200 jc @F short_done: jmp done @@: cmp x, 640 jnc short_done cld mov es, ega_segment lds si, sprite_ptr ; DS:SI -> sprite to be driven mov ax, 80 ; Calculate the offset mul y mov cx, x mov di, cx and cl, 7 ; CL = shift value mov shift, cl shr di, 3 add di, ax ; DI -> byte offset mov start_di, di mov ax, [si].e_height ; AX = height of the sprite in rows mov number_of_rows, ax ; Save for later use shl ax, 3 ; AX *= 8, to get size of 1 sprite plane mov sprite_plane_size, ax mov ax, number_of_rows ; Let's see if it goes too low add ax, y sub ax, BOTTOM_LINE jbe @F ; Skip if it doesn't sub number_of_rows, ax ; Update the number of rows @@: add si, E_BODY ; SI -> start of sprite body mov start_si, si cmp x, 640-32 ; See if we are going to be right ja short_mask ; Skip if no mask cmp shift, 0 jnz shifted mov bl, NUMBER_OF_PLANES next_plane: dec bl mov dx, GRAPHICS_12 ; Talk to EGA control logic mov al, 4 mov ah, bl out dx, ax mov dx, SEQUENCE_REG ; Set up the ports for writing as well mov ax, 100h + MAP_MASK_REG mov cl, bl shl ah, cl out dx, ax mov cx, number_of_rows @@: mov ax, es:[di] ; Get the background dword mov dx, es:[di+2] and ax, [si] ; Do the mask dword and dx, [si+4] or ax, [si+2] ; Bring on the sprite or dx, [si+6] mov es:[di], ax ; Replace with new graphic dword mov es:[di+2], dx add si, 8 ; Next row stuff add di, 80 loop @B mov di, start_di mov si, start_si add si, sprite_plane_size mov start_si, si or bl, bl jnz next_plane jmp done short_mask: jmp masked shifted: mov plane_number, NUMBER_OF_PLANES-1 mov cl, shift mov bh, -1 shr bh, cl next_shift_plane: mov dx, GRAPHICS_12 ; Talk to EGA control logic mov al, 4 mov ah, plane_number out dx, ax mov dx, SEQUENCE_REG ; Set up the ports for writing as well mov ax, 100h + MAP_MASK_REG mov cl, plane_number shl ah, cl out dx, ax mov ch, byte ptr number_of_rows mov cl, shift @@: lodsw ; Get the sprite mask xchg ah, al ; Switch them around ror ax, cl mov bl, ah ; Top CL bits are ones to mask 3rd byte not bh ; BH = ~BH and bl, bh or ah, bh ; Now set the top ones of source byte mov dx, es:[di] ; Get the first destination word xchg ah, al ; Re-order the mask bytes and dx, ax ; Mask it lodsw ; Get the sprite xchg ah, al ror ax, cl or dh, al ; OR DH w/ old AH mov al, ah ; Save the upper bits and ah, bh ; Mask off other bits not bh ; BH = BH and al, bh ; Cut out the rotunds or dl, al ; OR least sig. bytes mov es:[di], dx ; Save that first word, whew! mov dl, ah ; DL = pushed up sprite bits lodsw ; Get the next sprite mask xchg ah, al ; Switch them around ror ax, cl mov dh, ah ; DH = MS shifted mask bits and ah, bh ; Get rid of shifted bits or ah, bl ; OR with shifted mask, previous byte or dh, bh ; Add on the mask xchg ah, al ; AH:AL back to normal and es:[di+4], dh ; Easy way to get rid of DH mov bl, dl ; BL = previous sprite bits mov dx, es:[di+2] ; Get the destination word and dx, ax ; Mask it lodsw ; Get the sprite xchg ah, al ror ax, cl or dh, al ; OR DH w/ old AH mov al, ah ; Save the upper bits not bh ; BH = ~BH and ah, bh ; Mask off other bits not bh ; BH = BH and al, bh ; Cut out the rotunds or al, bl or dl, al ; OR least sig. bytes mov es:[di+2], dx ; Save that first word, whew! or es:[di+4], ah add di, 80 dec ch jnz @B mov di, start_di mov si, start_si add si, sprite_plane_size mov start_si, si sub plane_number, 1 jc @F jmp next_shift_plane @@: jmp done masked: xor ax, ax ; Set up masks and ORs mov mask_lsw, ax mov mask_msw, ax dec ax mov or_lsw, ax mov or_msw, ax cmp x, 640-24 ; See if we have masked it already jc @F ; Skip if we have mov mask_msw, 0ff00h mov or_msw, 0ffh cmp x, 640-16 jc @F mov mask_msw, -1 ; Make sure nothing gets masked mov or_msw, 0 cmp x, 640-8 ; See if that's all jc @F mov mask_lsw, 0ff00h mov or_lsw, 0ffh @@: mov plane_number, NUMBER_OF_PLANES-1 next_mask_plane: mov dx, GRAPHICS_12 ; Talk to EGA control logic mov al, 4 mov ah, plane_number out dx, ax mov dx, SEQUENCE_REG ; Set up the ports for writing as well mov ax, 100h + MAP_MASK_REG mov cl, plane_number shl ah, cl out dx, ax mov cx, number_of_rows @@: mov ax, [si] ; Get the first mask mov dx, [si+4] ; Get the second mask or ax, mask_lsw or dx, mask_msw and ax, es:[di] ; Get the background dword and dx, es:[di+2] mov bx, or_lsw ; Get the OR lsw and bx, [si+2] ; Bring on the sprite or ax, bx mov bx, or_msw and bx, [si+6] or dx, bx mov es:[di], ax ; Replace with new graphic dword mov es:[di+2], dx add si, 8 ; Next row stuff add di, 80 loop @B mov di, start_di mov si, start_si add si, sprite_plane_size mov start_si, si sub plane_number, 1 jnc next_mask_plane jmp done done: pop ds ret put_sprite endp ; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE ; void EGA_CLEAR_AREA( ui X1, ui Y1, ui X2, ui Y2 ) ; Clears an area on the EGA display to black ; Given: ; X1,Y1 = X,Y pixel coordinates of the upper left corner ; X2,Y2 = X,Y pixel coordinates of the lower right corner ; Returns: ; Rectangular area from X1,Y1 to X2,Y2 (inclusive) cleared to black public ega_clear_area ega_clear_area proc uses ds si di, x1:word, y1:word, x2:word, y2:word local height:word local di_start:word local word_columns:word local di_offset:word cld cmp x1, 640 ; See if too far to the right jc @F mov x1, 0 @@: cmp x2, 640 jc @F mov x2, 639 @@: mov ax, y2 ; Check out number of rows sub ax, y1 jnc @F ; See if jerk put them in backwards neg ax @@: inc ax mov bx, ax ; BX = number of rows mov height, ax mov ax, y1 ; Check our starting offset mov cx, 80 mov di_offset, cx mul cx mov di, ax mov ax, x1 ; See where we start shr ax, 3 add di, ax mov ax, x2 sub ax, x1 jnc @F neg ax @@: add ax, 16 shr ax, 4 jnz @F jmp done @@: mov word_columns, ax add ax, ax sub di_offset, ax mov es, ega_segment mov dx, SEQUENCE_REG ; Set up the ports for writing mov ax, 0f00h+MAP_MASK_REG out dx, ax mov dx, di_offset xor ax, ax mov di_start, di @@: mov cx, word_columns rep stosw add di, dx dec bx jnz @B done: ret ega_clear_area endp ; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE ; void EGA_INSTALL() ; Installs IRQ-2 vectors for EGA card ; Given: ; nothing ; Returns: ; -1 if neither EGA or VGA else ; EGA interrupt vector installed and enabled public ega_install ega_install proc mov ax, 40h ; ES -> video BIOS data area mov es, ax mov dx, es:[EGA_ADDRESS] ; DX -> CRTC Address port mov ega_base_port, dx ; Save the port address mov ax, 1a00h ; Read display combination int 10h cmp al, 1ah ; See if EGA jne ega_adaptor cmp bl, 7 ; See if VGA je vga_adaptor cmp bl, 8 je vga_adaptor error_out: mov ax, -1 jmp short done ega_adaptor: mov al, es:[CRT_MODE] ; AL = video BIOS mode number mov bx, offset EGA_settings xlat jmp short @F vga_adaptor: mov al, v_retrace_reg ; AL = Vertical retrace register out dx, al inc dx in al, dx @@: mov v_retrace_value, al mov done_page_flip, -1 mov do_page_flip, 0 xor ax, ax ; ES -> base page mov es, ax mov bx, 0ah*4 ; Vector for IRQ 2 mov dx, cs mov ax, offset ega_interrupt cli xchg es:[bx], ax xchg es:[bx+2], dx mov old_vector, ax mov old_vector+2, dx in al, 21h ; Get present mask mov old_irq_mask, al and al, 11111011b out 21h, al mov dx, ega_base_port mov ax, default_retrace and ah, 11001111b out dx, ax jmp short $+2 or ah, 00010000b out dx, ax sti done: ret ega_install endp ; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE ; void EGA_RIP_OUT() ; Undoes all the interrupt processing for the EGA ; Given: ; nothing ; Returns: ; EGA interrupt vector removed public ega_rip_out ega_rip_out proc uses es mov ax, old_vector ; See if installed or ax, old_vector+2 jz done ; Return if not installed xor ax, ax ; ES -> base page mov es, ax mov bx, 0ah*4 ; Vector for IRQ 2 mov ax, old_vector mov dx, old_vector+2 mov old_vector, 0 mov old_vector+2, 0 cli mov es:[bx], ax mov es:[bx+2], dx in al, 21h mov ah, old_irq_mask ; Restore old interrupt mask and ah, 1 shl 2 and al, 11111011b or al, ah out 21h, al mov dx, 3d4h mov ax, 2b11h out dx, ax sti done: ret ega_rip_out endp ; EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE ; void EGA_INTERRUPT( void ) ; Handles all the interrupt processing for the EGA controller ; Given: ; This is run at every IRQ-2 spike (ie. vertical retrace) ; Returns: ; If FLIP_TURN is brought to zero, EGA visual pages are swapped ; If swap is made, DO_PAGE_FLIP set to 0, DONE_PAGE_FLIP set to -1 ega_interrupt proc far push ax push dx push ds mov ax, @DATA mov ds, ax mov dx, 3c2h ; DX -> I/O port for input status in al, dx test al, 1 shl 7 jnz @F ; Interrupt is ours pushf call dword ptr [old_vector] jmp done @@: mov dx, ega_base_port ; DX -> EGA/VGA register port in al, dx push ax mov ax, default_retrace and ah, 11101111b out dx, ax jmp short $+2 mov al, EOI out EOI_PORT, al jmp short $+2 sti dec flip_turn jnz @F mov flip_turn, 4 cmp do_page_flip, 0 ; See if we need to do this jz @F mov al, 0ch ; Select register for page MSB mov ah, byte ptr ega_segment+1 shl ah, 4 out dx, ax ; Output the most significant byte xor ega_segment, 800h ; Swap the active page jmp short $+2 mov do_page_flip, 0 mov done_page_flip, -1 @@: cli mov ax, default_retrace and ah, 11011111b or ah, 00010000b out dx, ax jmp short $+2 pop ax out dx, al done: pop ds pop dx pop ax iret ega_interrupt endp end [LISTING TWO] comment \ Sprite Circle Handler Copyright (c) February 1989, Ryu Consulting, Inc. (916) 722 - 1939 anytime Written by Rahner James, CS \ ; **************************************************************************** ; EQUATES ; **************************************************************************** MAX_SPRITES equ 50 ; **************************************************************************** ; STRUCTURES ; **************************************************************************** sprite_structure struc animate_ptr dw 0,0 sprite_width dw 0 ; Width in words sprite_height dw 0 ; Height in pixels sprite_body db ? sprite_structure ends struc x dw 0 y dw 0 depth dw 0 pre_x dw 0 pre_y dw 0 dest_x dw 0 dest_y dw 0 adder_x dw 0 adder_y dw 0 sprite_ptr dw 0,0 next_node dw 0 sprite_node ends .data ; **************************************************************************** ; DATA VARIABLES and EXTERNAL DEFINITIONS ; **************************************************************************** extrn done_page_flip:byte, do_page_flip:byte extrn min_x:word, min_y:word, max_y:word, max_x:word first_sprite dw 0 public sprite_list sprite_list sprite_node MAX_SPRITES dup(<>) pre_max_x dw 639 pre_max_y dw 199 pre_min_x dw 0 pre_min_y dw 0 .code ; **************************************************************************** ; ROUTINES and EXTERNAL CODE DEFINITIONS ; **************************************************************************** extrn do_background:near, put_sprite:near ; **************************************************************************** ; void DO_SPRITE_LIST( void ) ; Sets up the sprites on the unviewed back page ; Given: ; A sprite list has been created and is stored in the array SPRITE_LIST ; Returns: ; If DONE_PAGE_FLIP is 0, no processing is done ; else all sprites in the sprite list are put on the non-visual page ; then DONE_PAGE_FLIP is set to 0 and DO_PAGE_FLIP is set to -1 do_sprite_list proc uses si cmp done_page_flip, 0 ; See if we need to do this jnz @F jmp done @@: call do_background mov ax, pre_max_x mov max_x, ax mov ax, pre_max_y mov max_y, ax mov ax, pre_min_x mov min_x, ax mov ax, pre_min_y mov min_y, ax xor ax, ax ; Clear out some variables mov pre_max_x, ax mov pre_max_y, ax dec ax mov pre_min_x, ax mov pre_min_y, ax mov si, first_sprite ; SI -> sprite node to start with next_sprite: or si, si ; See if this is a NULL pointer jnz @F jmp almost_done @@: mov ax, [si].x mov [si].pre_x, ax cmp [si].dest_x, ax ; See if we are already there je no_add_x ; Skip if we are mov cx, [si].dest_x ; Get our absolute value sub cx, [si].x jnc @F neg cx @@: add ax, [si].adder_x mov [si].x, ax sub ax, [si].dest_x jnc @F neg ax @@: cmp cx, ax jnc no_add_x mov ax, [si].dest_x mov [si].x, ax no_add_x: mov ax, [si].y mov [si].pre_y, ax cmp [si].dest_y, ax ; See if we are already there je no_add_y ; Skip if we are mov cx, [si].dest_y ; Get our absolute value sub cx, [si].y jnc @F neg cx @@: add ax, [si].adder_y mov [si].y, ax sub ax, [si].dest_y jnc @F neg ax @@: cmp cx, ax jnc no_add_y mov ax, [si].dest_y mov [si].y, ax no_add_y: cmp [si].y, 200 ; See if beyond bottom line jnc pre_next_sprite mov ax, [si].x ; See if we need to update some things cmp ax, 640 ; See if beyond right column jnc pre_next_sprite cmp ax, pre_min_x ; See if pre_x < min_x jnc @F ; Skip if not mov pre_min_x, ax ; Update with new MIN_X @@: mov ax, [si].y ; See if need to update min_y cmp ax, pre_min_y jnc @F mov pre_min_y, ax @@: les bx, dword ptr [si].sprite_ptr ; ES:BX -> sprite structure mov ax, es:[bx].sprite_width inc ax if (@Cpu AND 2) shl ax, 4 ; AX = AX * 16 else rept 4 add ax, ax endm endif add ax, [si].x cmp pre_max_x, ax ; See if > max_x jnc @F ; Jump if not mov pre_max_x, ax ; Assume we are going to save it @@: mov ax, es:[bx].sprite_height add ax, [si].y cmp pre_max_y, ax ; See if > max_x jnc @F ; Jump if not mov pre_max_y, ax ; Assume we are going to save it @@: push es ; Set up for call to put_sprite() push bx push [si].y push [si].x call put_sprite add sp, 4 ; Clear the stack pop bx ; ES:BX -> sprite pointer pop es les bx, dword ptr es:[bx].animate_ptr mov [si].sprite_ptr, bx mov [si].sprite_ptr+2, es pre_next_sprite: mov si, [si].next_node ; SI -> next sprite node in line jmp next_sprite almost_done: mov ax, pre_max_x cmp ax, 640 jb @F mov ax, 639 mov pre_max_x, ax @@: cmp pre_min_x, ax jb @F dec ax mov pre_min_x, ax @@: mov ax, pre_max_y cmp ax, 200 jb @F mov ax, 199 mov pre_max_y, ax @@: cmp pre_min_y, ax jb @F mov pre_min_y, ax @@: mov done_page_flip, 0 mov do_page_flip, -1 done: ret do_sprite_list endp ; **************************************************************************** ; void CLEAR_SPRITE_LIST( void ) ; Zeros out the present sprite list ; Given: ; nothing ; Returns: ; FIRST_SPRITE and SPRITE_LIST array are zeroed clear_sprite_list proc uses di cld mov di, offset sprite_list mov ax, ds mov es, ax xor ax, ax mov first_sprite, ax mov cx, (MAX_SPRITES * (size sprite_node))/2 rep stosw ret clear_sprite_list endp ; **************************************************************************** ; int INSERT_SPRITE( ui X1,ui Y1, ui D_X,ui D_Y, ui PLUS_X,ui PLUS_Y, ; ui THE_DEPTH, sprite_structure far *SPRITE ) ; Inserts the first sprite of a sprite circle into the linked list of circles ; Given: ; X1,Y1 = pixel location of the upper left corner of the sprite ; D_X,D_Y = pixel location of the destination of the sprite ; PLUS_X,PLUS_Y = pixels the sprite moves every page flip ; THE_DEPTH = apparent distance of the sprite from the viewer ; SPRITE -> sprite to insert ; Returns: ; If 0, sprite pointer was inserted in the array ; else !0 if there is no room insert_sprite proc uses si, x1:word,y1:word, d_x:word,d_y:word,\ plus_x:word,plus_y:word, the_depth:word,\ sprite:dword mov cx, MAX_SPRITES mov bx, (offset sprite_list) - (size sprite_node) @@: add bx, size sprite_node mov ax, [bx].sprite_ptr ; See if this has been set yet or ax, [bx].sprite_ptr+2 loopnz @B jnz done_bad les ax, sprite ; ES:AX -> sprite location mov [bx].sprite_ptr, ax ; Save it mov [bx].sprite_ptr+2, es mov ax, x1 ; Set up the structure mov [bx].x, ax mov [bx].pre_x, ax mov ax, y1 mov [bx].y, ax mov [bx].pre_y, ax mov ax, d_x mov [bx].dest_x, ax mov ax, d_y mov [bx].dest_y, ax mov ax, plus_x mov [bx].adder_x, ax mov ax, plus_y mov [bx].adder_y, ax mov ax, the_depth mov [bx].depth, ax ; This is used in the following loop mov dx, bx ; Save this s[rite entry for later mov si, first_sprite ; SI -> sprite furthest from the viewer mov bx, offset first_sprite ; BX -> previous sprite entry mov cx, MAX_SPRITES @@: or si, si ; See if it's a NULL ptr jz @F ; Skip out if it is cmp ax, [si].depth ; See if farther from observer jnc @F mov bx, si ; BX = this pointer mov si, [si].next_node ; SI -> next sprite node in line loop @B done_bad: mov ax, -1 ; Indicate we had a problem jmp short done @@: xchg si, dx ; SI -> sprite_list[i] mov [si].next_node, dx cmp bx, offset first_sprite jne @F mov [bx], si jmp short done_good @@: mov [bx].next_node, si done_good: xor ax, ax done: ret insert_sprite endp ; **************************************************************************** ; void ADD_SPRITE( sprite_structure far *DEST, far *SOURCE ) ; Adds a self-relative sprite motion to the end of a sprite circle ; Given: ; DEST -> sprite circle header ; SOURCE -> sprite to add on ; Returns: ; SOURCE is added to the end of the sprite linked list and the ; circle ends are rejoined, may the circle be unbroken (ie Johnny Cash) add_sprite proc uses si di ds, dest:dword, source:dword lds si, dest les di, source next_sprite: mov ax, [si].animate_ptr ; See if this is the end of the line cmp ax, word ptr dest jne @F mov ax, [si].animate_ptr+2 cmp ax, word ptr dest+2 je got_the_end @@: lds si, [si].animate_ptr jmp next_sprite got_the_end: mov [si].animate_ptr, di mov [si].animate_ptr+2, es mov ax, word ptr dest mov es:[di].animate_ptr, ax mov ax, word ptr dest+2 mov es:[di].animate_ptr+2, ax ret add_sprite endp end [LISTING THREE] /****************************************************************************** TITLE: SPRITES.C Displays a sprite file on an EGA screen Written by: Rahner James, CS of Ryu Consulting, Inc. ******************************************************************************/ #include #include #include /****************************************************************************** VARIOUS DEFINITIONS ******************************************************************************/ #pragma pack(1) typedef unsigned char uc; typedef unsigned int ui; typedef unsigned long ul; /****************************************************************************** EXTERNAL DECLARATIONS ******************************************************************************/ extern void ega_convert(); extern ui ega_calculate( uc far * ); extern void ega_install(); extern void ega_clear_area( ui, ui, ui, ui ); /****************************************************************************** GLOBAL DATA ******************************************************************************/ ui min_x=0, min_y=0, max_x=639, max_y=199; /****************************************************************************** long READ_ALL_FILE( uc *FILENAME, uc huge *BUFFER, ul BUFFER_SIZE ) Opens and reads an entire sprite file Given: FILENAME -> name of sprite file to read BUFFER -> buffer to read the sprite file into BUFFER_SIZE = number of bytes the buffer can hold Returns: File is opened, read and closed Number of bytes read, if all went well If error, returns -1 ******************************************************************************/ long read_all_file( uc *filename, uc huge *buffer, ul buffer_size ) { long rv = 0; ui handle, dos_return, amount_read; ui amount_to_read; if ( _dos_open( filename, O_RDONLY, &handle ) ) return -1; while ( buffer_size ) { amount_to_read = buffer_size<60000L ? buffer_size : 60000L; if ( _dos_read( handle, buffer+rv, amount_to_read, &amount_read ) ) { rv = -1; break; } rv += amount_read; if ( amount_read < 60000 ) break; buffer_size -= amount_read; } _dos_close( handle ); return rv; } /****************************************************************************** void DO_BACKGROUND( void ) Sets up the background for the sprite visual screen Given: nothing Returns: visual sprite screen erased ******************************************************************************/ void do_background( void ) { ega_clear_area( min_x, min_y, max_x, max_y ); } /****************************************************************************** uc SET_MODE( uc MODE_NUMBER ) Sets the video mode Given: Mode number to set video to Returns: Present video mode number ******************************************************************************/ uc set_mode( uc mode_number ) { uc rv; union REGS regs; regs.h.ah = 15; int86( 0x10, ®s, ®s ); rv = regs.h.al; regs.h.ah = 0; regs.h.al = mode_number; int86( 0x10, ®s, ®s ); return rv; } /****************************************************************************** MAIN( int ARGC, uc *ARGV[] ) Allocates memory, reads in a sprite file, displays the sprites until a key is pressed, frees up memory and interrupt vectors Given: ARGC = number of command line values, must be > 1 ARGV[1] -> file name of the sprite file to display Returns: 0 if all went well, otherwise numbered according to error ******************************************************************************/ main( int argc, uc *argv[] ) { ui i, x, y; uc far *file_ptr, huge *sprite_start, huge *buffer_start; uc huge *sprite_ptr[20]; uc old_mode; ui memory_segment; ul memory_size=0, file_size; /* Check initial values and allocate memory for buffers */ if ( argc<2 ) { printf( "\nNo file name has been given\n" ); exit( 1 ); } if ( _dos_allocmem( -1, (ui *)&memory_size ) ) { if ( _dos_allocmem( memory_size, &memory_segment ) ) { printf( "\nMemory allocation error\n" ); exit( 2 ); } } else { memory_segment = memory_size; memory_size = 0xffff; } memory_size <<= 4; buffer_start = (uc huge *)((ul)memory_segment << 16L); /* Read in the sprite file and then convert it to our intenal structure */ file_ptr = buffer_start; if ( (file_size=read_all_file(argv[1],file_ptr,memory_size)) == -1 ) { _dos_freemem( memory_segment ); printf( "\nGot error reading %s. Aborting.\n", argv[1] ); exit( 3 ); } clear_sprite_list(); sprite_start = file_ptr + file_size; for ( i=0 ; i<20 && file_size ; ++i ) { ega_convert( sprite_ptr[i]=sprite_start, file_ptr ); x = ega_calculate( file_ptr ); sprite_start += x; x = (ui)*file_ptr * (ui)*(file_ptr+2) + 4; file_ptr += x; if ( file_size > (ul)x ) file_size -= (ul)x; else file_size = 0; } /* Create linked list of sprite circles */ insert_sprite( 100,100, 100,100, 0,0, 7, sprite_ptr[0] ); for ( x=1 ; x Sequencer register MOV AL, 1 ; AL = index 1, Clocking Mode OUT DX, AL INC DX ; DX -> Sequencer index port ŠMOV AL, 5 ; AL = to put in Clocking Mode OUT DX, AL Examplå 2º Replacinç thå codå iî Examplå 1 MOV DX, 3C4h ; DX -> Sequencer reg. pair MOV AX, 501h ; AL = index 1, AH = value 5 ŠOUT DX, AX ; Puts AL out 3C4h, then ; AH out 3C5h