OK, here's the first one, it requires the mask to immediately follow the sprite. This one was for an 8x7 cursor i had, so you might want to change the 7s to 8s if you want to draw an 8x8 sprite. Also, my graph buffer had an extra 16x16 column (14 bytes wide), so you might need to adjust some of the width values. For example, the multiplication at the beginning, the ld de,14 for the aligned loop, and the ld de,13 at the ebd if the notAligned loop.
;l = y
;a = x
;ix = sprite
putSpriteMasked:
ld b,7
ld h,0
ld e,l
ld d,h ;ld de,hl
add hl, hl ;x2
add hl, de ;x3
add hl, hl ;x6
add hl, de ;x7
add hl, hl ;x14
ld e,a ;guardar e en a para sacar el offset x
srl e
srl e
srl e ;/8
add hl,de
ld de,gbuf
add hl,de
ld (cursorGbufLoc),hl
push hl
push bc
ex af,af'
;copy the bytes beneath the cursor
ld a,b ;sprite height
ld de,cursorGbufSave
ldi ;load gbuf into gbuf buffer ()
ldi ;guardar dos bytes debajo del cursor
ld bc,12
add hl,bc ;próxima fila del gbuf
dec a
jr nz,$-9
ex af,af'
pop bc
pop hl
and $07 ;x offset
ld c,a
or a ;si x offset=0, significa que el sprite está alineado
jr nz,maskedNotAlignedLoop
ld de,14
maskedAlignedLoop:
ld a,(hl)
and (ix+7)
xor (ix)
ld (hl),a ;guardar
inc ix ;próximo byte
add hl,de ;próxima fila del gbuf
djnz maskedAlignedLoop
ret
maskedNotAlignedLoop:
push bc ;b = número de iteraciones restantes, c = xoffset
ld b,c
ld a,(ix+7) ;máscara
ld c,$FF ;dummy byte
ld d,(ix)
ld e,0
mNARotate:
scf
rra ;a = mask
rr c ;c = overflow
srl d ;d = sprite byte
rr e ;e = sprite overflow
djnz mNARotate
;mask = ac
;sprite = de
and (hl)
xor d
ld (hl),a ;primer byte
inc hl
ld a,c
and (hl)
xor e
ld (hl),a ;segundo byte del sprite
ld de,13
add hl,de ;próxima fila
inc ix
pop bc
djnz maskedNotAlignedLoop
ret
This is a modified 8xY version of that that supports variable height masks and with simple y clipping (only for the bottom, but adding it for the top is really easy) and x boundary detection, ie it won't draw the sprite if a pixel is offscreen.
;e = x
;l = y
;a = sprite height
draw_sprite_mask12:
ld a,12
draw_sprite_mask:
ld (aligned_mask),a ;update masks
ld (unaligned_mask),a ;masks should be at the end of the sprite (sprite+sprite_height)
ld d,a
ld a,e ;check x
cp 89
ret nc
ld a,l ;check y offscreen and simple clipping
or a
jp p,+_
neg
ld c,a
ld b,0
add ix,bc
neg
add a,d ;a negative Y + height will tell us the new height (pixels on screen)
ld d,a ;save new height (y+height)
ld l,0 ;set y coord to 0
_ add a,d
cp 65
ret nc
ld a,d
ld h,0
ld d,h
ld c,l
ld b,h ;ld bc,hl
add hl,hl ; *2
add hl,bc ; *3
add hl,hl ; *6
add hl,hl ;Y*12
ld b,a ;sprite height to b
ld a,e
srl e
srl e
srl e ;/8
add hl,de
ld de,gbuf
add hl,de ;gbuf + y offset + x offset
and $07 ;x offset
ld c,a
or a ;if x offset=0, sprite's aligned
jr nz,maskedNotAlignedLoop
ld de,12
maskedAlignedLoop:
ld a,(hl) ;gbuf byte
aligned_mask = $+2
and (ix+12) ;mix the mask
xor (ix) ;fill in with the sprite
ld (hl),a ;update
inc ix ;next byte
add hl,de ;next row
djnz maskedAlignedLoop
ret
maskedNotAlignedLoop:
push bc ;b = iterations left, c = x offset
ld b,c
unaligned_mask = $+2
ld a,(ix+12) ;mask
ld c,$FF ;dummy byte
ld d,(ix)
ld e,0
mNARotate:
scf
rra ;a = mask
rr c ;c = overflow
srl d ;d = sprite byte
rr e ;e = sprite overflow
djnz mNARotate
;mask = ac
;sprite = de
and (hl) ;mask1 + gbuf1
xor d ;sprite1
ld (hl),a
inc hl
ld a,c
and (hl) ;mask2 + gbuf2
xor e ;sprite2
ld (hl),a
ld de,11
add hl,de ;next row gbuf
inc ix ;next row sprite
pop bc
djnz maskedNotAlignedLoop
ret
EDIT: The second one is for a normal 12-byte width gbuf.