151
ASM / Re: ASM Optimized routines
« on: August 28, 2019, 09:33:29 am »
Here is a masked sprite routine (no clipping)! Interleave the data with the mask, where the MASK is ANDed with the buffer, and the data is ORed on top of that:
I also made some "bigsprite" routines! These do have clipping, too. First, they use some common subroutines for computing masks and performing most of the clipping and shifting:
Code: [Select]
;Masked Sprite routine
putsprite_masked:
;Inputs:
; (A,L) = (x,y)
; B is height
; IX points to the sprite data
; first byte is the data
; second byte is mask
; continues, alternating like this.
;
;Outputs:
; Mask is ANDed to the buffer, then data is ORed on top of that.
;
;Destroys:
; AF, BC, DE, HL, IX
;
;Notes:
; To set a pixel...
; black: mask is any, data is 1
; white: mask is 0, data is 0
; clear: mask is 1, data is 0 (keeps the data from the buffer)
;
;This routine is free to use :)
;65 bytes (or 66 bytes if gbuf is not located at 0x**40
ld e,l
ld h,0
ld d,h
add hl,hl
add hl,de
add hl,hl
add hl,hl
ld e,a
and 7
ld c,a
xor e ;essentially gets E with the bottom 3 bits reset
#if (plotSScreen&255) = 64
inc a
rra
rra
rra
ld e,a
ld d,plotSScreen>>8
#else
rra
rra
rra
ld e,a
add hl,de
ld de,plotSScreen
#endif
add hl,de
putsprite_masked_loop:
push bc
xor a
ld d,(ix)
ld e,a
sub c
ld b,c
ld c,$FF
inc ix
ld a,(ix)
jr z,putsprite_masked_rotdone
putsprite_masked_rot:
scf
rra
rr c
srl d
rr e
djnz putsprite_masked_rot
putsprite_masked_rotdone:
and (hl)
or d
ld (hl),a
inc hl
ld a,(hl)
and c
or e
ld (hl),a
ld c,11
add hl,bc
inc ix
pop bc
djnz putsprite_masked_loop
ret
But if you want even faster and smaller, use a non-traditional mask technique by ORing the mask onto the buffer, then XORing the data on top of it. The format is less intuitive, but it allows for white/black/clear/invert instead of just white/black/clear:Code: [Select]
;Masked Sprite routine
putsprite_masked:
;Inputs:
; (A,L) = (x,y)
; B is height
; IX points to the sprite data
; first byte is the data
; second byte is mask
; continues, alternating like this.
;
;Outputs:
; Mask is ORed to the buffer, then data is XORed on top of that.
;
;Destroys:
; AF, BC, DE, HL, IX
;
;Notes:
; To set a pixel...
; black: mask is 1, data is 0
; white: mask is 1, data is 1
; clear: mask is 0, data is 0 (keeps the data from the buffer)
; invert: mask is 0, data is 1 (inverts the data from the buffer)
;
;This routine is free to use :)
;63 bytes (or 64 bytes if gbuf is not located at 0x**40
ld e,l
ld h,0
ld d,h
add hl,hl
add hl,de
add hl,hl
add hl,hl
ld e,a
and 7
ld c,a
xor e ;essentially gets E with the bottom 3 bits reset
#if (plotSScreen&255) = 64
inc a
rra
rra
rra
ld e,a
ld d,plotSScreen>>8
#else
rra
rra
rra
ld e,a
add hl,de
ld de,plotSScreen
#endif
add hl,de
putsprite_masked_loop:
push bc
xor a
ld d,(ix)
ld e,a
or c
ld b,c
ld c,e
inc ix
ld a,(ix)
jr z,putsprite_masked_rotdone
putsprite_masked_rot:
rra
rr c
srl d
rr e
djnz putsprite_masked_rot
putsprite_masked_rotdone:
or (hl)
xor d
ld (hl),a
inc hl
ld a,(hl)
or c
xor e
ld (hl),a
ld c,11
add hl,bc
inc ix
pop bc
djnz putsprite_masked_loop
ret
I also made some "bigsprite" routines! These do have clipping, too. First, they use some common subroutines for computing masks and performing most of the clipping and shifting:
Code: [Select]
;133 bytes total
;This is made by Zeda, feel free to use it for whatever.
;Takes inputs for a big sprite and sets up masks and clipping
;requires 4 bytes of temporary RAM, but doesn't use SMC
spritetmp = 8000h ;relocate this as needed! Just need 4 bytes.
sprite_width = spritetmp+0
sprite_x = spritetmp+1
sprite_mask0 = spritetmp+2
sprite_mask1 = spritetmp+3
bigsprite_subroutine:
;Inputs:
; B is the X-coordinate
; C is the Y-Coordinate
; DE points to the sprite
; H is the height
; L is the width in bytes
;Outputs:
; carry flag is set if okay to draw, nc if out-of-bounds.
; B is height.
; C is width.
; HL points to the byte to start drawing at.
; DE points to where to start sourcing the sprite data
; (sprite_width) is the width of the sprite in bytes
; (sprite_x) is the intitial x coordinate to begin drawing at
; (sprite_mask0) is the left mask
; (sprite_mask1) is the right mask
;92 bytes
;First check if the sprite is on-screen in the horizontal direction
ld a,c
cp 64
jr c,+_
add a,h
ret nc
ld h,a
push hl
xor a
ld h,a
sub c
ex de,hl
add hl,de
dec a
jr nz,$-2
ex de,hl
pop hl
xor a
ld c,a
_:
;Next check h+c<=64
ld a,64
sub c
cp h
jr nc,+_
ld h,a
_:
;Make sure the height is not now 0
ld a,h
or a
ret z
;Save the width and height of the sprite
push hl ;height,width
ld h,b
ld (sprite_width),hl ;x,width
push de ;sprite pointer
;Set up a pointer to the routine for shifting the routine for shifting the sprite data
ld ixh,rshiftHA_7>>8
ld a,h
cpl
and 7
ld l,a
add a,a
add a,l
add a,rshiftHA_7&255
ld ixl,a
#if (rshiftHA_7&255)>234
jr nc,$+4
inc ixh
#endif
ld a,b
and 7
ld de,spritemask
add a,e
ld e,a
#if spritemask&255>248
jr nc,$+3
inc d
#endif
ld a,(de)
ld (sprite_mask0),a
cpl
ld (sprite_mask1),a
;
;
ld a,c
add a,a
sbc a,a
ld h,a
ld a,b
ld b,h
ld l,c
add hl,hl
add hl,bc
add hl,hl
add hl,hl
ld c,a
add a,a
sbc a,a
ld b,a
ld a,c
sra c
sra c
sra c
add hl,bc
ld bc,plotSScreen
add hl,bc
pop de
pop bc
;B is height
;C is width
ex de,hl
scf
ret
rshiftHA_7:
rr h \ rra
rr h \ rra
rr h \ rra
rr h \ rra
rr h \ rra
rr h \ rra
rr h \ rra
ex de,hl
ld e,a
ret
spritemask:
.db $00,$80,$C0,$E0,$F0,$F8,$FC,$FE
call_ix:
jp (ix)
Then you can draw a big sprite with OR logic:Code: [Select]
bigsprite_OR:
;Inputs:
; B is the X-coordinate
; C is the Y-Coordinate
; DE points to the sprite
; H is the height
; L is the width in bytes
;68 bytes
;Set up the clipping
call bigsprite_subroutine
ret nc
bigsprite_OR_loop:
push bc ;height,width
push de ;gbuf ptr
push hl ;sprite data pointer
ld a,(sprite_x)
ld c,a
add a,8
ld (sprite_x),a
spriteloop_OR:
push bc
push hl
ld h,(hl)
xor a
call call_ix
ld a,c
cp 96
jr nc,+_
ld a,(hl)
or d
ld (hl),a
ld a,c
_:
inc hl
add a,8
cp 96
jr nc,+_
ld a,(sprite_mask1)
ld a,(hl)
or e
ld (hl),a
_:
ld bc,11
add hl,bc
ex de,hl
pop hl
ld a,(sprite_width)
ld c,a
add hl,bc
pop bc
djnz spriteloop_OR
pop hl
inc hl
pop de
inc de
pop bc
dec c
jr nz,bigsprite_OR_loop
ret
Or draw with XOR logic:Code: [Select]
bigsprite_XOR:
;Inputs:
; B is the X-coordinate
; C is the Y-Coordinate
; DE points to the sprite
; H is the height
; L is the width in bytes
;68 bytes
;Set up the clipping
call bigsprite_subroutine
ret nc
bigsprite_XOR_loop:
push bc ;height,width
push de ;gbuf ptr
push hl ;sprite data pointer
ld a,(sprite_x)
ld c,a
add a,8
ld (sprite_x),a
spriteloop_XOR:
push bc
push hl
ld h,(hl)
xor a
call call_ix
ld a,c
cp 96
jr nc,+_
ld a,(hl)
xor d
ld (hl),a
ld a,c
_:
inc hl
add a,8
cp 96
jr nc,+_
ld a,(sprite_mask1)
ld a,(hl)
xor e
ld (hl),a
_:
ld bc,11
add hl,bc
ex de,hl
pop hl
ld a,(sprite_width)
ld c,a
add hl,bc
pop bc
djnz spriteloop_XOR
pop hl
inc hl
pop de
inc de
pop bc
dec c
jr nz,bigsprite_XOR_loop
ret
Or draw with AND logic:Code: [Select]
bigsprite_AND:
;Inputs:
; B is the X-coordinate
; C is the Y-Coordinate
; DE points to the sprite
; H is the height
; L is the width in bytes
;69 bytes
;Set up the clipping
call bigsprite_subroutine
ret nc
bigsprite_AND_loop:
push bc ;height,width
push de ;gbuf ptr
push hl ;sprite data pointer
ld a,(sprite_x)
ld c,a
add a,8
ld (sprite_x),a
spriteloop_AND:
push bc
push hl
ld h,(hl)
scf \ sbc a,a
call call_ix
ld a,c
cp 96
jr nc,+_
ld a,(hl)
and d
ld (hl),a
ld a,c
_:
inc hl
add a,8
cp 96
jr nc,+_
ld a,(sprite_mask1)
ld a,(hl)
and e
ld (hl),a
_:
ld bc,11
add hl,bc
ex de,hl
pop hl
ld a,(sprite_width)
ld c,a
add hl,bc
pop bc
djnz spriteloop_AND
pop hl
inc hl
pop de
inc de
pop bc
dec c
jr nz,bigsprite_AND_loop
ret
Or draw with Erase logic:Code: [Select]
bigsprite_Erase:
;Inputs:
; B is the X-coordinate
; C is the Y-Coordinate
; DE points to the sprite
; H is the height
; L is the width in bytes
;67 bytes
;Set up the clipping
call bigsprite_subroutine
ret nc
bigsprite_Erase_loop:
push bc ;height,width
push de ;gbuf ptr
push hl ;sprite data pointer
ld a,(sprite_x)
ld c,a
add a,8
ld (sprite_x),a
spriteloop_Erase:
push bc
push hl
ld h,(hl)
xor a
call call_ix
ld a,c
cp 96
jr nc,+_
ld a,d
cpl
and (hl)
ld (hl),a
ld a,c
_:
inc hl
add a,8
cp 96
jr nc,+_
ld a,e
cpl
and (hl)
ld (hl),a
_:
ld bc,11
add hl,bc
ex de,hl
pop hl
ld a,(sprite_width)
ld c,a
add hl,bc
pop bc
djnz spriteloop_Erase
pop hl
inc hl
pop de
inc de
pop bc
dec c
jr nz,bigsprite_Erase_loop
ret
Or draw with Overwrite logic:Code: [Select]
bigsprite_Overwrite:
;Inputs:
; B is the X-coordinate
; C is the Y-Coordinate
; DE points to the sprite
; H is the height
; L is the width in bytes
;71 bytes
;Set up the clipping
call bigsprite_subroutine
ret nc
bigsprite_Overwrite_loop:
push bc ;height,width
push de ;gbuf ptr
push hl ;sprite data pointer
ld a,(sprite_x)
ld c,a
add a,8
ld (sprite_x),a
spriteloop_Overwrite:
push bc
push hl
ld h,(hl)
xor a
call call_ix
ld a,c
cp 96
jr nc,+_
ld a,(sprite_mask0)
and (hl)
or d
ld (hl),a
ld a,c
_:
inc hl
add a,8
cp 96
jr nc,+_
ld a,(sprite_mask1)
and (hl)
or e
ld (hl),a
_:
ld bc,11
add hl,bc
ex de,hl
pop hl
ld a,(sprite_width)
ld c,a
add hl,bc
pop bc
djnz spriteloop_Overwrite
pop hl
inc hl
pop de
inc de
pop bc
dec c
jr nz,bigsprite_Overwrite_loop
ret