Z80 Routines:Graphic:largesprite
From WikiTI
The Largesprite routine is used to copy the contents of a variable sized sprite to the Graph Buffer.
Code
Here is Joe Wingbermuehle's version, which is the one used in ION. Gbuf must be defined before its use.
;======================= ;LargeSprite ;by Joe Wingbermuehle ;======================= ;Does: Copy a sprite to the gbuf ;Input: ix=sprite address, a='x', l='y', b='height' (in pixels), c='width' (in bytes, e.g. 2 would be 16) ;Output: The sprite is copied to the gbuf ;----------------------- largeSprite: di ;turn interrupts off (we want to use shadow registers) ex af,af' ;exchange af with af' \ ld a,c ;ld c in a (a = 'width') | for not destroying a ('x') push af ;push a | ex af,af' ;exchange back | and 'width' is now in a' (saved) ld e,l ;e = 'y' ld h,$00 ;h = 0 ld d,h ;d = 0 add hl,de ;'y' *2 \ add hl,de ; *3 | calculate 'y' *12 because 'y' is 'in rows' add hl,hl ; *6 | (screen is 12 bytes in length) add hl,hl ; *12 / ld e,a ;e = 'x' and $07 ;and %00000111 ld c,a ;last 3 bits in c (amount of bits to shift all bytes) srl e ;e/2 | shifting e ('x') 3 bits to the right srl e ; /4 | %11111111 becomes %00011111 for example srl e ; /8 / add hl,de ;hl = 'y'; de = 'x' (rounded) | add them ld de, gbuf ;de = the adress of graph buffer add hl,de ;add hl to the adress of the gbuf largeSpriteLoop1: push hl ;save adress largeSpriteLoop2: ld d,(ix) ;first sprite data in d ld e,$00 ;e = 0 ld a,c ;a = c (to not destroy c) or a ;is a = 0? (same as cp 0) jr z,largeSpriteSkip1 ;if theres nothing to shift (a = 0) loop it largeSpriteLoop3: srl d ;shift one bit to the right; put the destroyed bit in the carry flag rr e ;put the carry flag in e (%00000000 becomes %10000000 if carry flag = 1) dec a ;decrease counter (with was 'the amount of bits to shift') jr nz,largeSpriteLoop3 ;if the counter is not 0 loop back largeSpriteSkip1: ld a,(hl) ;graphbyte in a xor d ;xor first byte of sprite (that can be changed to 'or d' if you want a OR-routine) ld (hl),a ;back to buffer inc hl ;increase pointer ld a,(hl) ;graphbyte in a xor e ;xor with shifted sprite byte (change to 'or e' for OR-routine) ld (hl),a ;back to buffer inc ix ;increase sprite adress ex af,af' ;exchange af with af' ( a is now the 'width' from the first line) dec a ;decrease 'width' push af ;push the 'width' ex af,af' ;exchange back pop af ;pop the 'width' jr nz,largeSpriteLoop2 ;if a is not 0 (if a = 0 then we would be done) loop it pop hl ;pop gbuf adress (search the last push hl!) pop af ;pop | to restore the real 'width' push af ;push / ex af,af' ;af' must be the original 'width' when loop 'largeSpriteLoop1' ld de,$0C ;ld de,12 add hl,de ;next line djnz largeSpriteLoop1 ;if not b = 0 loop (b = height of sprite) pop af ;pop because we dont want a stack problem :) ret ;return
Example
;... ld l,8 ;y ld a,16 ;x ld b,8 ;height ld c,2 ;width in bytes ld ix,sprite call largesprite call fastcopy ;... sprite: .db %11111111,%11111111 .db %10000000,%00000001 .db %10000000,%00000001 .db %10000000,%00000001 .db %10000000,%00000001 .db %10000000,%00000001 .db %10000000,%00000001 .db %11111111,%11111111
Version without shadow registers
by Tijl Coosemans, made for Venus. Compatible with ION's routine. screenBuf must be defined.
iLargeSprite ld h,0 ld d,h ld e,l add hl,de add hl,de add hl,hl add hl,hl ld e,a srl e srl e srl e add hl,de ld de,screenBuf add hl,de and 7 ld e,a iLargeSpriteLoop1 push bc push hl ld b,c iLargeSpriteLoop2 ld c,(ix) ld d,0 inc ix ld a,e or a jr z,iLargeSprite1 iLargeSpriteLoop3 srl c rr d dec a jr nz,iLargeSpriteLoop3 iLargeSprite1 ld a,c xor (hl) ld (hl),a inc hl ld a,d xor (hl) ld (hl),a djnz iLargeSpriteLoop2 pop hl ld c,12 add hl,bc pop bc djnz iLargeSpriteLoop1 ret
Version with Clipping
This is a version that supports clipping the large sprite. It's considerable larger and requires Self modifying code. The SMC can be removed without to much difficulty. It has different inputs than ION's.
;-------------------------------- ;Clip Big Sprite ;by James Montelongo ;MAX SIZE: 64x64 ;ix - Sprite ;b - height ;c - width in bytes ;d - x ;e - y ClipBigSprite: ; Early out, Check if its even remotely on screen ld a,e cp 64 ret p add a,b ret m ret z ld a,d cp 96 ret p ld a,c add a,a add a,a add a,a add a,d ret m ret z ld a,e or a jp p,Check_clip_bottom neg push de ld hl,0 ld d,l ld e,a bit 2,c jr z,$+2+1 add hl,de add hl,hl bit 1,c jr z,$+2+1 add hl,de add hl,hl bit 0,c jr z,$+2+1 add hl,de pop de ex de,hl add ix,de ;Here you can save the top offset ex de,hl ld e,0 neg add a,b ld b,a Check_clip_bottom: ld a,e add a,b sub 64 jp m,Check_clip_Left neg add a,b ld b,a Check_clip_Left: ; at this point you may want to save b xor a ld (bigskip),a ld a,Clipleftsize ld (Do_Clipleft),a ld a,d or a jp p,Check_clip_right cpl and $F8 rra rra rra ex de,hl ;save the clipped left offset ld e,a ld d,0 add ix,de ld (bigskip),a ex de,hl inc a neg add a,c ld c,a xor a ld (Do_Clipleft),a ld a,d and $07 ld d,a Check_clip_right: ld a,Cliprightsize ld (Do_Clipright),a ld a,c add a,a add a,a add a,a add a,d sub 96 jp m,Check_clip_middle and $F8 rra rra rra ld l,a ld a,(bigskip) add a,l inc a ld (bigskip),a neg add a,c ld c,a xor a ld (Do_Clipright),a Check_clip_middle: ; This is where C should be saved. xor a ld (Do_ClipMiddle),a ld a,c or a jp nz,dontskipmiddle ld a,ClipMiddlesize ld (Do_ClipMiddle),a dontskipmiddle: ld l,e ld a,d ld h,0 ld d,h add hl,hl add hl,de add hl,hl add hl,hl ld e,a and $07 xor 7 ld (BigRot1),a ld (BigRot2),a ld (BigRot3),a add a,a ld (clipbigrot1),a ld a,$ff clipbigrot1 = $+1 jr $ srl a srl a srl a srl a srl a srl a srl a srl e srl e srl e add hl,de ld de,gbuf add hl,de ; This is where gbuf offset should be saved. ld d,a cpl ld e,a ;masks should be saved to BigSpriteRow: push bc push hl ld b,c Do_Clipleft = $+1 jr Clipleft ld a,(ix) inc ix BigRot1 = $+1 jr $ rrca rrca rrca rrca rrca rrca rrca BigMask0: and e or (hl) ld (hl),a Clipleft: Clipleftsize = Clipleft-(Do_Clipleft+1) Do_ClipMiddle = $+1 jr $+2 BigSpriteloop: ld a,(ix) inc ix BigRot2 = $+1 jr $ rrca rrca rrca rrca rrca rrca rrca ld c,a BigMask1: and d or (hl) ld (hl),a inc hl ld a,c BigMask2: and e or (hl) ld (hl),a djnz BigSpriteloop ClipMiddle: ClipMiddlesize = ClipMiddle-(Do_ClipMiddle+1) Do_ClipRight = $+1 jr ClipRight ld a,(ix) BigRot3 = $+1 jr $ rrca rrca rrca rrca rrca rrca rrca BigMask3: and d or (hl) ld (hl),a ClipRight: Cliprightsize = ClipRight-(Do_ClipRight+1) pop hl ld bc,12 ;width of the screen add hl,bc bigskip = $+1 ld bc,0 add ix,bc pop bc djnz BigSpriteRow ret