;========================================================================= ; Sprite routines by David Phillips http://www.acz.org/ ;========================================================================= ; All sprites are 8x8 masked sprites ; ; PutSprite -- black/white (sprite, mask) ; PutSpriteClip -- black/white with clipping (sprite, mask) ; GraySprite -- grayscale (dark, light, mask) ; GraySpriteClip -- grayscale with clipping (dark, light, mask) ; ; in: hl = pointer to sprite, bc = x,y location ; out: af, bc, de, hl, ix = trash ; size: 250 bytes ;========================================================================= VideoDraw = VirtScr ; [1024] virtual screen or video ram GrayDraw1 = GrayMem1 ; [1024] dark grayscale plane GrayDraw2 = GrayMem2 ; [1024] light grayscale plane ;========================================================================= ; in: hl = 8x8 masked sprite, bc = x,y location PutSpriteClip: ex de,hl ; save pointer in de ld hl,PutSpriteE ; need to call putsprite ld (ClipSpriteE),hl ; modify call address jr ClipSprite ; jump to clipsprite ; in: hl = 8x8 masked grayscale sprite, bc = x,y location GraySpriteClip: ex de,hl ; save pointer in de ld hl,GraySpriteE ; need to call graysprite ld (ClipSpriteE),hl ; modify call address ; in: de = sprite pointer, bc = x,y location ClipSprite: ld a,b ; get x coord cp 249 ; hanging off left edge? jr nc,ClipSpriteL ; then clip left edge cp 128 ; completely off right edge? ret nc ; then don't draw it jr ClipSpriteR ; clip right edge ClipSpriteL: res 7,b ; draw on right edge dec c ; move up a row (left clipping) bit 7,c ; are we on the top edge? jr z,ClipSpriteA ; if not, skip top fix ld hl,RenderSpriteCV ; point to rows to draw inc (hl) ; increase it (decreased later) dec de ; decrease sprite pointer ld hl,-16 ; need to draw up a row ld (ClipSpriteX),hl ; modify top row fix value ClipSpriteA: ld hl,$0218 ; change instruction to ld (RenderSpriteCL),hl ; modify to skip drawing left byte jr ClipSpriteH ; done clipping horizontal ClipSpriteR: cp 120 ; hanging off right edge? jr c,ClipSpriteH ; if not, don't clip ld hl,$0218 ; change instruction to ld (RenderSpriteCR),hl ; modify to skip drawing right byte ClipSpriteH: ld a,c ; get y coord cp 249 ; hanging off top? jr nc,ClipSpriteT ; then clip the top cp 63 ; completely off the bottom? jr nc,ClipSpriteF ; then don't draw, but fix everything jr ClipSpriteB ; clip the bottom ClipSpriteT: xor a ; clear for subtract ld h,a ; clear high byte sub c ; calc bytes above screen ld c,h ; set y coord to 0 ld l,a ; set low byte to bytes above screen add hl,de ; advance sprite pointer ex de,hl ; swap back into de jr ClipSpriteV ; finish vertical clipping ClipSpriteB: sub 57 ; calc bytes below screen jr c,ClipSpriteD ; don't clip if not below screen inc a ; increase bytes below screen ClipSpriteV: neg ; negate so it can be subtracted ClipSpriteZ =$+1 ld hl,RenderSpriteCV ; point to rows to draw add a,(hl) ; add (subtract) from current value ld (hl),a ; save new value ClipSpriteD: push de ; push sprite pointer pop ix ; pop sprite pointer in ix call FindAddress ; calc address and offset of sprite ClipSpriteX =$+1 ld de,0 ; load address fix for top row add hl,de ; add the fix value ClipSpriteE =$+1 call $ffff ; call the correct sprite routine ClipSpriteF: ld a,8 ; normally 8 rows are drawn ld (RenderSpriteCV),a ; set rows to draw back ld hl,$a678 ; normal opcode is ld (RenderSpriteCL),hl ; set instructions back inc l ; change to ld (RenderSpriteCR),hl ; set instructions back ld hl,0 ; normally top row fix is 0 ld (ClipSpriteX),hl ; set rox fix back ret ; return from clipping ; in: hl = 8x8 masked sprite, bc = x,y location PutSprite: push hl ; push sprite pointer pop ix ; pop in ix call FindAddress ; calc address and offset PutSpriteE: ld de,VideoDraw ; draw sprite to video memory add hl,de ; add offset to buffer start jr RenderSprite ; actually draw the sprite ; in: hl = 8x8 masked grayscale sprite, bc = x,y location GraySprite: push hl ; push sprite pointer pop ix ; pop in ix call FindAddress ; calc address and offset GraySpriteE: push hl ; save address offset push ix ; save sprite pointer ld de,GrayDraw1 ; draw to first gray buffer add hl,de ; add offset to buffer start ld a,16 ; grayscale sprites are 16 bytes ld (RenderSpriteM),a ; adjust sprite load instruction call RenderSprite ; draw the sprite ld a,8 ; second sprite is 8 bytes from mask ld (RenderSpriteM),a ; adjust sprite load instruction back pop ix ; restore sprite pointer pop hl ; restore address offset ld de,GrayDraw2 ; now draw to second gray buffer add hl,de ; add offset to second buffer start ld de,$0008 ; sprites are 8 bytes add ix,de ; advance sprite pointer ; in: hl = address in buffer, c = offset, ix = sprite RenderSprite: RenderSpriteCV =$+1 ld b,8 ; set row counter to 8 bytes RenderSpriteL: push bc ; save row counter and sprite offset ld a,(ix+8) ; load mask for this row RenderSpriteM =$-1 cpl ; invert the mask ld b,a ; save inverted mask ld d,(ix) ; load sprite byte for this row inc ix ; advance sprite pointer ld a,c ; set shift counter to sprite offset ld c,$ff ; set all bits of right mask byte ld e,0 ; clear right byte of sprite or a ; is the sprite aligned? jr z,RenderSpriteD ; then skip shifting it RenderSpriteS: srl d ; shift left sprite byte rr e ; into the right sprite byte scf ; set carry rr b ; shift left mask byte rr c ; into the right mask byte dec a ; decrease shift counter jr nz,RenderSpriteS ; loop until counter is 0 RenderSpriteD: RenderSpriteCL =$ ld a,b ; load the left sprite byte and (hl) ; and it with the screen or d ; or it with the mask ld (hl),a ; set screen memory to left byte inc hl ; increase screen pointer RenderSpriteCR =$ ld a,c ; load the right sprite byte and (hl) ; and it with the screen or e ; or it with the mask ld (hl),a ; set screen memory to left byte ld de,15 ; next row is 15 bytes ahead add hl,de ; move screen pointer to next row pop bc ; restore loop counter and shift mask djnz RenderSpriteL ; loop for all bytes in sprite ret ; done drawing the sprite ; in: bc = x,y location ; out: hl = offset of byte, c = offset in video byte FindAddress: ld a,c ; get the y coord add a,a ; multiply by 2 add a,a ; again by 4 ld l,a ; save as low byte ld h,0 ; clear high byte add hl,hl ; multiply by 8 add hl,hl ; and again by 16 ld a,b ; now get the x coord and %11111000 ; clear lower 3 bits rra ; divide by 2 rra ; this time by 4 rra ; and finally by 8 or l ; combine with low byte ld l,a ; save it ld a,b ; get x coord again and %00000111 ; clear all but last 3 bits ld c,a ; save sprite offset in c ret ; return to caller .end