Z80 Routines:Graphic:largesprite

From WikiTI
Revision as of 14:15, 26 October 2009 by Galandros (Talk | contribs)

Jump to: navigation, search

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