Suppose you want an 8-bit counter, ideally in A:
xor a ;performs 'ld a,0', but does affect flags, which isn't a problem here.
loop:
;Do stuff, but return with A unchanged
;Probably requires 'push af \ do stuff \ pop af'
;or 'ld (var_A),a \ do stuff \ ld a,(var_A)'
inc a
cp 10
jr nz,loop ;use jp if the loop is too far
;loop is finished, so return or do whatever you were going to do.
If you need a 16-bit counter, ideally in HL, it is a similar process:
ld hl,0
loop:
;Do stuff, but return with HL unchanged
;Probably requires 'push hl \ do stuff \ pop hl'
;or 'ld (var_HL),hl \ do stuff \ ld hl,(var_HL)'
inc hl
;push de ;use if you need to preserve DE
ld de,10
or a ;\
sbc hl,de ; |This essentially compares HL and DE.
add hl,de ;/
;pop de ;use if you need to preserve DE
jr nz,loop ;use 'jp' if the jump is too far.
;loop is finished, so return or do whatever you were going to do.