; SPRITE routines MAX_SPRITE EQU 100 RECTANGLE STRUCT 2,NONUNIQUE X WORD 0 Y WORD 0 Wid4 BYTE 0 Ht BYTE 0 Color BYTE 0 Next WORD 0 ; DrawMe is used to not bother with sprites that you know ; are contained totally within another, allowing animated ; eyes, etc to be stored in separate sprites. These will be ; drawn to the local buffer but skipped when copying to the ; screen, so if they are not TOTALLY contained, they will ; just get clipped away. DrawMe BYTE 1 ; default, yes draw me. ; (Storage from this point on ... NEVER provide anything but ; default for these values!) address_virt WORD 0 address_buf WORD 0 next_line_virt WORD 0 next_line_buf WORD 0 RECTANGLE ENDS SPRITE STRUCT 2, NONUNIQUE RECTANGLE <> ; Contains rectangle info SPRITE ENDS EVEN rect5 SPRITE <<40 ,60 , 2,8, C_TRANSPARENT, 0 , 0>> rect4 SPRITE <<80 ,30 , 2,8, C_TRANSPARENT, offset rect5, 0>> rect3 SPRITE <<120,60 , 2,8, C_TRANSPARENT, offset rect4, 0>> rect2 SPRITE <<55 ,100, 2,8, C_TRANSPARENT, offset rect3, 0>> rect1 SPRITE <<105,100, 2,8, C_TRANSPARENT, offset rect2, 0>> rect6 SPRITE <<36 ,56 , 4,16, C_BLUE, offset rect1, 1>> rect7 SPRITE <<76 ,26 , 4,16, C_BLUE, offset rect6, 1>> rect8 SPRITE <<116,56 , 4,16, C_BLUE, offset rect7, 1>> rect9 SPRITE <<51 ,96 , 4,16, C_BLUE, offset rect8, 1>> rect10 SPRITE <<101,96 , 4,16, C_BLUE, offset rect9, 1>> ;; Simply adding in these 5 rectangles (~20000 pixels for both ;; drawing and erasing) really slows things down! That's why ;; it's important to optimize the sprite drawing routines! rect11 SPRITE <<35 ,55 ,14,36, C_GREEN, offset rect10, 1>> rect12 SPRITE <<75 ,25 ,14,36, C_GREEN, offset rect11, 1>> rect13 SPRITE <<115,55 ,14,36, C_GREEN, offset rect12, 1>> rect14 SPRITE <<50 ,95 ,14,36, C_GREEN, offset rect13, 1>> rect15 SPRITE <<100,95 ,14,36, C_GREEN, offset rect14, 1>> FIRST_SPRITE EQU rect10 EVEN AnimateSprites PROC near ret ; Blank out the draw page, by copying from the blank page ; to the draw page all rectangles which had changed. The ; blank page must always be entirely blank if this is going ; to work! mov di,cs:DrawPage.UpperLeftAddress add di,cs:DrawPage.ScrollOffset mov si,cs:BlankPage.UpperLeftAddress add si,cs:BlankPage.ScrollOffset mov bp,cs:BlankPage.Rectangles call CopyRectangles ; Now draw the sprites. Uses a temporary buffer to ensure ; minimal drawing to the screen, but that's not really necessary, ; if memory is at a minimum. It's just faster... mov bp,offset FIRST_SPRITE mov cs:DrawPage.Rectangles,bp call do_fill_buffer mov di,cs:DrawPage.UpperLeftAddress add di,cs:DrawPage.ScrollOffset mov bh,cs:DrawPage.AlignmentMask mov bp,offset FIRST_SPRITE jmp smart_rects ; "call" AnimateSprites ENDP smart_dest DW 0 out_di DW 0 out_si DW 0 EVEN smart_rects PROC near add di,cs:DrawPage.Address mov ds,cs:segBuffer mov es,cs:segVideo mov dx,3c4h mov al,02h out dx,al inc dx mov cs:smart_dest,di ; === Beginning of loop through rectangles! === sp_nextrect: cmp cs:[bp].RECTANGLE.DrawMe,1 jne sp_next ; Draw this rectangle from the buffer to screen memory. ; Calculate the output address. mov si,cs:[bp].RECTANGLE.address_buf mov di,cs:[bp].RECTANGLE.address_virt add di,cs:smart_dest ; Loop over 4 planes mov bl,4 sp_plane_loop: mov al,bh out dx,al mov cs:out_di,di mov cs:out_si,si ; Loop over height mov ch,cs:[bp].RECTANGLE.Ht sp_row_loop: ; Loop over width of rectangle (Wid4 is actually width/4) mov cl,cs:[bp].RECTANGLE.Wid4 sp_col_loop: ; Read a byte from the buffer ; Is it transparent (no-modify)? If so, just jump over the draw mov al,byte ptr ds:[si] cmp al,C_TRANSPARENT je sp_next_pixel ; Otherwise, draw it on the spreen, and mark it transparent ; so that it won't be drawn again. mov byte ptr es:[di],al mov byte ptr ds:[si],C_TRANSPARENT ; Skip to next 4-byte group (next column that can be drawn in ; Mode X) Also increment spreen draw address, but only by 1 ; because ModeX is 4 pixels per byte sp_next_pixel: add si,4 inc di dec cl jnz sp_col_loop ; End of row. Skip space to get to left edge of next row down ; Skip SI = (SCREEN_WIDTH - #bytesdrawn) ; Only draw up to height of rectangle add si,cs:[bp].RECTANGLE.next_line_buf add di,cs:[bp].RECTANGLE.next_line_virt dec ch jnz sp_row_loop mov di,cs:out_di mov si,cs:out_si inc si rol bh,1 adc di,0 dec bl jnz sp_plane_loop ; Follow chain to next rectangle sp_next: mov bp,cs:[bp].RECTANGLE.Next cmp bp,0 jne sp_nextrect ; All done sp_end: ret smart_rects ENDP ; BP -> first rectangle. Follows BP->next, stops when BP = 0 EVEN do_fill_buffer PROC near mov es,cs:segBuffer cmp bp,0 je fill_end fill_loop: mov bx,cs:[bp].RECTANGLE.Y shl bx,1 ; BX = word index y mov di,cs:MultBufWidth[bx] ; DI = SW * y mov cx,cs:[bp].RECTANGLE.X ; CX = x add di,cx ; DI = (SW * y) + x mov cs:[bp].RECTANGLE.address_buf,di ; (DI used later) mov ax,cs:MultVirtWidth[bx] ; AX = (VW/4) * y shr cx,2 ; CX = (x / 4) add ax,cx ; AX = (VW * y + x)/4 mov cs:[bp].RECTANGLE.address_virt,ax mov dx,(VIRTUAL_WIDTH / 4) sub dl,cs:[bp].RECTANGLE.Wid4 ; DX = (VW - w) / 4 mov cs:[bp].RECTANGLE.next_line_virt,dx mov dx,(SCREEN_WIDTH / 4) sub dl,cs:[bp].RECTANGLE.Wid4 ; DX = (SW - w) / 4 shl dx,2 ; DX = SW - w mov cs:[bp].RECTANGLE.next_line_buf,dx mov ah,cs:[bp].RECTANGLE.Color mov al,cs:[bp].RECTANGLE.Color mov ch,cs:[bp].RECTANGLE.Ht fill_row_loop: mov cl,cs:[bp].RECTANGLE.Wid4 fill_col_loop: mov es:[di],ax mov es:[di+2],ax add di,4 dec cl jnz fill_col_loop add di,dx dec ch jnz fill_row_loop mov bp,cs:[bp].RECTANGLE.Next cmp bp,0 jne fill_loop fill_end: ret do_fill_buffer ENDP EVEN CopyRectangles PROC near mov ax,cs:segVideo mov ds,ax mov es,ax ; Calculate the difference between the source and destination ; pages. Since in a movsb loop the two would remain a constant ; distance apart, we can just calculate a displacement and then ; not have to worry about SI; instead use DI and DI+BX, thanks ; to the thoughtful x86 ALU! mov bx,di sub bx,si mov dx,GC_INDEX mov ax,ALL_COPY_BITS out dx,ax mov dx,SC_INDEX mov ax,0F02h out dx,ax mov si,di ;store destination ; === Beginning of loop through rectangles! === cr_nextrect: cmp cs:[bp].RECTANGLE.DrawMe,1 jne cr_next ; Draw this rectangle from the buffer to screen memory. ; Calculate the output address. mov di,cs:[bp].RECTANGLE.address_virt mov dx,cs:[bp].RECTANGLE.next_line_virt add di,si ; Loop over height mov ch,cs:[bp].RECTANGLE.Ht cr_row_loop: ; Loop over width of rectangle (Wid4 is actually width/4) mov cl,cs:[bp].RECTANGLE.Wid4 cr_col_loop: mov al,ds:[di + bx] stosb dec cl jnz cr_col_loop mov al,ds:[di + bx] mov es:[di],al ; End of row. Skip space to get to left edge of next row down ; Only draw up to height of rectangle add di,dx dec ch jnz cr_row_loop ; Follow chain to next rectangle cr_next: mov bp,cs:[bp].RECTANGLE.Next cmp bp,0 jne cr_nextrect ; All done cr_end: mov dx,GC_INDEX mov ax,ALL_DRAW_BITS out dx,ax ret CopyRectangles ENDP