X-Git-Url: http://4ch.mooo.com/gitweb/?a=blobdiff_plain;f=16%2Fscrasm%2FSPRITE.INC;fp=16%2Fscrasm%2FSPRITE.INC;h=9c3f2e48143b9678d089031df369a305f23d030e;hb=b50a0eb714c64dee65050539243e02ef2aa308b5;hp=0000000000000000000000000000000000000000;hpb=c703087fbf8156953e47763ca81fd07227e3358c;p=16.git diff --git a/16/scrasm/SPRITE.INC b/16/scrasm/SPRITE.INC new file mode 100644 index 00000000..9c3f2e48 --- /dev/null +++ b/16/scrasm/SPRITE.INC @@ -0,0 +1,280 @@ +; 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 + + \ No newline at end of file