--- /dev/null
+; 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