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