├── README.md ├── 16bitsetbit.asm ├── abshl.asm ├── strlen.asm ├── plot.asm ├── reversebits.asm ├── mulsigned.asm ├── xorshift.asm ├── isqrt.asm ├── scrollytext.asm ├── attribscroll.asm ├── bresenham.asm ├── filledcircle.asm └── fastisqr.asm /README.md: -------------------------------------------------------------------------------- 1 | # Z80 Snippets 2 | -------------------------------------------------------------------------------- /16bitsetbit.asm: -------------------------------------------------------------------------------- 1 | ; set bit a (0..15) of the bc register (bc |= 2^a) 2 | ; self-modifying code 3 | 4 | rlca 5 | rlca 6 | cp %00100000 7 | rla 8 | or %11000000 9 | ld (SETBIT+1),a 10 | SETBIT: 11 | set 0,b ; this instruction gets modified 12 | -------------------------------------------------------------------------------- /abshl.asm: -------------------------------------------------------------------------------- 1 | ; abs(hl) by John Metcalf 2 | ; 10 bytes, 20/39 cycles 3 | 4 | ; call with hl = signed number 5 | ; returns hl = abs(hl) 6 | ; corrupts a 7 | 8 | bit 7,h 9 | jr z,absdone 10 | sub a 11 | sub l 12 | ld l,a 13 | sbc a,a 14 | sub h 15 | ld h,a 16 | absdone: 17 | -------------------------------------------------------------------------------- /strlen.asm: -------------------------------------------------------------------------------- 1 | ; strlen(hl) by John Metcalf 2 | ; 11 bytes, 52+21*strlen cycles 3 | 4 | ; call with hl = address of zero-terminated string 5 | ; returns hl = length of string 6 | ; corrupts a 7 | 8 | xor a 9 | ld b,a 10 | ld c,a 11 | cpir 12 | 13 | ; hl = ~bc 14 | 15 | ld a,c 16 | cpl 17 | ld l,a 18 | ld a,b 19 | cpl 20 | ld h,a 21 | -------------------------------------------------------------------------------- /plot.asm: -------------------------------------------------------------------------------- 1 | ; self-modifying plot by John Metcalf 2 | ; plot d = x-axis, e = y-axis, self-modifying code 3 | 4 | ld a,d 5 | cpl 6 | add a,a 7 | add a,a 8 | add a,a 9 | or %11000110 10 | ld (PLOTBIT+1),a 11 | ld a,e 12 | rra 13 | scf 14 | rra 15 | or a 16 | rra 17 | ld l,a 18 | xor e 19 | and %11111000 20 | xor e 21 | ld h,a 22 | ld a,l 23 | xor d 24 | and %11111000 25 | xor l 26 | rrca 27 | rrca 28 | rrca 29 | ld l,a 30 | PLOTBIT: 31 | set 0,(hl) ; modified 32 | ret 33 | -------------------------------------------------------------------------------- /reversebits.asm: -------------------------------------------------------------------------------- 1 | ; REVERSEBITS - reverse the bits in a 2 | ; on entry: a = bit pattern to reverse 3 | ; on exit: a = reversed bits, l = corrupt, other registers preserved 4 | ; author: John Metcalf 5 | ; length: 17 bytes 6 | ; time: 66 cycles 7 | ; http://www.retroprogramming.com/2014/01/fast-z80-bit-reversal.html 8 | 9 | ld l,a ; a = 76543210 10 | rlca 11 | rlca ; a = 54321076 12 | xor l 13 | and 0xAA 14 | xor l ; a = 56341270 15 | ld l,a 16 | rlca 17 | rlca 18 | rlca ; a = 41270563 19 | rrc l ; l = 05634127 20 | xor l 21 | and 0x66 22 | xor l ; a = 01234567 23 | 24 | -------------------------------------------------------------------------------- /mulsigned.asm: -------------------------------------------------------------------------------- 1 | ; 16 bit signed multiply by John Metcalf 2 | ;31 bytes 3 | ;min: 928cc 4 | ;max: 1257cc 5 | ;avg: 1088.5cc 6 | 7 | ; call with bc, de = 16 bit signed numbers to multiply 8 | ; returns de:hl = 32 bit signed product 9 | ; corrupts a 10 | 11 | ; de:hl = bc*de 12 | 13 | multiply: 14 | xor a 15 | ld h,a 16 | ld l,a 17 | bit 7,d 18 | jr z,muldpos 19 | sbc hl,bc 20 | muldpos: 21 | 22 | or b 23 | jp p,mulbpos 24 | sbc hl,de 25 | mulbpos: 26 | 27 | ld a,16 28 | mulloop: 29 | add hl,hl 30 | rl e 31 | rl d 32 | jr nc,mul0bit 33 | add hl,bc 34 | jr nc,mul0bit 35 | inc de 36 | mul0bit: 37 | dec a 38 | jr nz,mulloop 39 | -------------------------------------------------------------------------------- /xorshift.asm: -------------------------------------------------------------------------------- 1 | ; 16-bit xorshift pseudorandom number generator by John Metcalf 2 | ; 20 bytes, 86 cycles (excluding ret) 3 | 4 | ; returns hl = pseudorandom number 5 | ; corrupts a 6 | 7 | ; generates 16-bit pseudorandom numbers with a period of 65535 8 | ; using the xorshift method: 9 | 10 | ; hl ^= hl << 7 11 | ; hl ^= hl >> 9 12 | ; hl ^= hl << 8 13 | 14 | ; some alternative shift triplets which also perform well are: 15 | ; 6, 7, 13; 7, 9, 13; 9, 7, 13. 16 | 17 | org 32768 18 | 19 | xrnd: 20 | ld hl,1 ; seed must not be 0 21 | 22 | ld a,h 23 | rra 24 | ld a,l 25 | rra 26 | xor h 27 | ld h,a 28 | ld a,l 29 | rra 30 | ld a,h 31 | rra 32 | xor l 33 | ld l,a 34 | xor h 35 | ld h,a 36 | 37 | ld (xrnd+1),hl 38 | 39 | ret 40 | -------------------------------------------------------------------------------- /isqrt.asm: -------------------------------------------------------------------------------- 1 | ; 16 bit isqrt by John Metcalf 2 | ; 29 bytes, 652-722 cycles (average 687) 3 | 4 | ; call with hl = number to square root 5 | ; returns a = square root 6 | ; corrupts hl, de, b 7 | 8 | ; note: I've seem similar code credited to Milos Bazelides, 9 | ; Ricardo Bittencourt and uncredited. This implementation works 10 | ; with the bitwise complement of the root to save a few cycles. 11 | 12 | ld de,0FFC0h ; 10 13 | ld a,l ; 4 14 | ld l,h ; 4 15 | ld h,0 ; 7 16 | ld b,7 ; 7 17 | sqrloop: 18 | add hl,de ; 11 19 | jr c,sqrbit ; 12 / 7 20 | sbc hl,de ; 15 21 | sqrbit: 22 | ccf ; 4 23 | rl d ; 8 24 | add a,a ; 4 25 | adc hl,hl ; 15 26 | add a,a ; 4 27 | adc hl,hl ; 15 28 | djnz sqrloop ; 13 / 8 29 | 30 | ld a,d ; 4 31 | cpl ; 4 32 | add hl,de ; 11 33 | rla ; 4 34 | -------------------------------------------------------------------------------- /scrollytext.asm: -------------------------------------------------------------------------------- 1 | ; text scroller, 69 bytes 2 | ; John Metcalf 3 | 4 | org 32768 5 | 6 | scrolly: 7 | ld hl,message 8 | 9 | nextchar: 10 | ld a,(hl) 11 | or a 12 | jr z,scrolly 13 | push hl 14 | 15 | ; get bitmap for glyph 16 | 17 | ld l,a 18 | ld h,0 19 | add hl,hl 20 | add hl,hl 21 | add hl,hl 22 | ld de,(23606) 23 | add hl,de 24 | 25 | ; copy glyph to workspace 26 | 27 | ld de,tempchar 28 | ld bc,8 29 | ld a,c 30 | ldir 31 | 32 | scroll: 33 | halt 34 | ld hl,050FFh 35 | ld de,tempchar 36 | ld c,8 37 | nextrow: 38 | ex de,hl 39 | rl (hl) 40 | ex de,hl 41 | ld b,32 42 | push hl 43 | scrollrow: 44 | rl (hl) 45 | dec l 46 | djnz scrollrow 47 | pop hl 48 | inc h 49 | inc de 50 | dec c 51 | jr nz,nextrow 52 | dec a 53 | jr nz,scroll 54 | 55 | pop hl 56 | inc hl 57 | 58 | ; test for keypress 59 | 60 | ld bc,07FFEh 61 | in a,(c) 62 | rrca 63 | jr c,nextchar 64 | ret 65 | 66 | message: 67 | db "Z80 Assembly for the ZX Spectrum... ",0 68 | 69 | tempchar: 70 | db 0,0,0,0,0,0,0,0 71 | -------------------------------------------------------------------------------- /attribscroll.asm: -------------------------------------------------------------------------------- 1 | ; attribute scroller, 79 bytes 2 | ; John Metcalf 3 | 4 | org 32768 5 | 6 | attrscr: 7 | ld hl,message 8 | 9 | nextchar: 10 | ld a,(hl) 11 | or a 12 | jr z,attrscr 13 | push hl 14 | 15 | ; get bitmap for glyph 16 | 17 | ld l,a 18 | ld h,0 19 | add hl,hl 20 | add hl,hl 21 | add hl,hl 22 | ld de,(23606) 23 | add hl,de 24 | 25 | ; copy glyph to workspace 26 | 27 | ld de,tempchar 28 | ld bc,8 29 | ld a,c 30 | ldir 31 | 32 | scroll: 33 | push af 34 | halt 35 | 36 | ; shift everything left by 1 attribute 37 | 38 | ld de,05900h 39 | ld hl,05901h 40 | ld c,0FFh 41 | ldir 42 | 43 | ; copy a column of bits from the glyph to the attributes 44 | 45 | ld de,tempchar 46 | ld hl,0591Fh 47 | ld a,8 48 | ld c,32 49 | nextrow: 50 | ld (hl),b 51 | ex de,hl 52 | rl (hl) 53 | ex de,hl 54 | jr nc,zerobit 55 | ld (hl),54 56 | zerobit: 57 | add hl,bc 58 | inc de 59 | dec a 60 | jr nz,nextrow 61 | pop af 62 | dec a 63 | jr nz,scroll 64 | 65 | pop hl 66 | inc hl 67 | 68 | ; test for keypress 69 | 70 | ld bc,07FFEh 71 | in a,(c) 72 | rrca 73 | jr c,nextchar 74 | ret 75 | 76 | message: 77 | db "Z80 Assembly for the ZX Spectrum... ",0 78 | 79 | tempchar: 80 | db 0,0,0,0,0,0,0,0 81 | -------------------------------------------------------------------------------- /bresenham.asm: -------------------------------------------------------------------------------- 1 | ; draw lines using self-modifying Bresenham's algorithm 2 | ; by Arcadiy Gobuzov and John Metcalf 3 | ; de = end1 (d = x-axis, e = y-axis) 4 | ; hl = end2 (h = x-axis, l = y-axis) 5 | 6 | DRAW: 7 | ld a,d 8 | sub h 9 | ex de,hl ; swap ends so de (temporarily) has the lowest x-axis coord 10 | jr c,DRAW 11 | push af 12 | 13 | ld bc,$1C16 ; b = inc e / c-1 = dec d 14 | 15 | SWAP: 16 | dec c ; c-1 -> dec d, c-2 -> inc d 17 | ld a,e 18 | sub l 19 | ex de,hl ; swap ends so de has the lowest y-axis coord 20 | jr c,SWAP 21 | 22 | pop hl ; h = x-length of line (delta_x) 23 | ld l,a ; l = y-length of line (delta_y) 24 | 25 | cp h 26 | jr nc,SHALLOW 27 | ld b,c 28 | ld c,$1C ; c = $1C inc e 29 | ld l,h ; swap deltas 30 | ld h,a 31 | ld a,l 32 | or a 33 | SHALLOW: 34 | 35 | ld (ADVANCE),bc 36 | rra ; a = max(delta_x,delta_y) / 2 37 | ld b,l 38 | inc b ; b = max(delta_x,delta_y) + 1 39 | 40 | LOOP: 41 | ld c,a 42 | 43 | ; ----------------------------------------------- 44 | 45 | ; plot d = x-axis, e = y-axis, self-modifying code 46 | 47 | push hl 48 | ld a,d 49 | cpl 50 | add a,a 51 | add a,a 52 | add a,a 53 | or %11000110 54 | ld (PLOTBIT+1),a 55 | ld a,e 56 | rra 57 | scf 58 | rra 59 | or a 60 | rra 61 | ld l,a 62 | xor e 63 | and %11111000 64 | xor e 65 | ld h,a 66 | ld a,l 67 | xor d 68 | and %11111000 69 | xor l 70 | rrca 71 | rrca 72 | rrca 73 | ld l,a 74 | PLOTBIT: 75 | set 0,(hl) ; modified 76 | pop hl 77 | 78 | ; ----------------------------------------------- 79 | 80 | ld a,c 81 | sub h 82 | jr nc,ADVANCE+1 83 | add a,l 84 | ADVANCE: 85 | inc d ; modified 86 | inc e ; modified 87 | djnz LOOP 88 | ret 89 | -------------------------------------------------------------------------------- /filledcircle.asm: -------------------------------------------------------------------------------- 1 | ; FAST FILLED CIRCLES - Sinclair Spectrum, displays filled circles 2 | ; on entry, d = x-centre, e = y-centre, c = radius 3 | ; on exit, a, bc, de, hl corrupt 4 | ; Length 106 - John Metcalf, Sun 28 Feb 1999 5 | 6 | ; Draws filled circles very quickly. An assortment of tricks have 7 | ; been used to achieve this. This new version is over 10% faster 8 | ; than the previous after further optimizing the inner loops. 9 | 10 | 11 | org 40000 12 | ld de,96*257 13 | ld c,64 14 | 15 | ld b,0 16 | ld a,e ; subtract radius from y-centre 17 | inc c 18 | sub c 19 | ld e,a 20 | 21 | ld h,b ; hl = 0 22 | ld l,b 23 | 24 | NEXT: 25 | push bc 26 | push hl 27 | 28 | ld bc,65280 ; triangular root :-) 29 | CALC: 30 | dec c 31 | add hl,bc 32 | jr c,CALC 33 | 34 | ld a,e ; calc pos in screen memory 35 | cp 192 36 | jr nc,OFFSCR 37 | rrca 38 | scf 39 | rra 40 | rrca 41 | ld l,a 42 | xor e 43 | and 88 44 | xor e 45 | and 95 46 | ld h,a 47 | 48 | ld a,d 49 | add a,c 50 | rl c 51 | ld b,a 52 | xor l 53 | and 7 54 | xor b 55 | rrca 56 | rrca 57 | rrca 58 | ld l,a 59 | 60 | xor a ; calc first part byte (if any) 61 | sub b 62 | and 7 63 | jr z,NOHALF 64 | ld b,a 65 | xor a 66 | dec c 67 | VAIN: 68 | inc c 69 | jr z,ROTB 70 | scf 71 | rla 72 | djnz VAIN 73 | inc c 74 | or (hl) 75 | ld (hl),a 76 | inc hl 77 | NOHALF: 78 | xor a ; calc full bytes (if any) 79 | sub c 80 | rrca 81 | rrca 82 | rrca 83 | and 31 84 | jr z,NOFULL 85 | ld b,a 86 | ld a,255 87 | DRAW: 88 | ld (hl),a 89 | inc hl 90 | djnz DRAW 91 | NOFULL: 92 | ld a,c ; calc final part byte (if any) 93 | and 7 94 | jr z,OFFSCR 95 | ld b,a 96 | ld a,255 97 | ROTB: 98 | add a,a 99 | djnz ROTB 100 | DISPLAY: 101 | or (hl) 102 | ld (hl),a 103 | 104 | OFFSCR: 105 | pop hl 106 | pop bc 107 | inc e ; next line 108 | dec c 109 | jr nz,SKIP 110 | dec bc 111 | SKIP: 112 | adc hl,bc 113 | jr nz,NEXT 114 | ret 115 | -------------------------------------------------------------------------------- /fastisqr.asm: -------------------------------------------------------------------------------- 1 | ; fast 16-bit isqrt by Zeda Thomas and John Metcalf 2 | ; 87 bytes, 333-370 clock cycles (average 351.5) 3 | 4 | ; original was 92 bytes, 344-379 cycles (average 362) 5 | ; http://www.retroprogramming.com/2017/07/a-fast-z80-integer-square-root.html 6 | 7 | ; call with hl = number to square root 8 | ; returns a = square root 9 | ; corrupts hl, de 10 | 11 | ; ---------- 12 | 13 | ld de,05040h ; 10 14 | ld a,h ; 4 15 | sub e ; 4 16 | jr nc,sq7 ;\ 17 | add a,e ; | branch 1: 12cc 18 | ld d,16 ; | branch 2: 18cc 19 | sq7: ;/ 20 | 21 | ; ---------- 22 | 23 | cp d ; 4 24 | jr c,sq6 ;\ 25 | sub d ; | branch 1: 12cc 26 | set 5,d ; | branch 2: 19cc 27 | sq6: ;/ 28 | 29 | ; ---------- 30 | res 4,d ; 8 31 | srl d ; 8 32 | set 2,d ; 8 33 | cp d ; 4 34 | jr c,sq5 ;\ 35 | sub d ; | branch 1: 12cc 36 | set 3,d ; | branch 2: 19cc 37 | sq5: ;/ 38 | srl d ; 8 39 | 40 | ; ---------- 41 | 42 | inc a ; 4 43 | sub d ; 4 44 | jr nc,sq4 ;\ 45 | dec d ; | branch 1: 12cc 46 | add a,d ; | branch 2: 19cc 47 | dec d ; | <-- this resets the low bit of D, so `srl d` resets carry. 48 | sq4: ;/ 49 | srl d ; 8 50 | ld h,a ; 4 51 | 52 | ; ---------- 53 | 54 | ld a,e ; 4 55 | sbc hl,de ; 15 56 | jr nc,sq3 ;\ 57 | add hl,de ; | 12cc or 18cc 58 | sq3: ;/ 59 | ccf ; 4 60 | rra ; 4 61 | srl d ; 8 62 | rra ; 4 63 | 64 | ; ---------- 65 | 66 | ld e,a ; 4 67 | sbc hl,de ; 15 68 | jr c,sq2 ;\ 69 | or 20h ; | branch 1: 23cc 70 | db 254 ; | <-- start of `cp *` which is 7cc to skip the next byte. 71 | sq2: ; | branch 2: 21cc 72 | add hl,de ;/ 73 | 74 | xor 18h ; 7 75 | srl d ; 8 76 | rra ; 4 77 | 78 | ; ---------- 79 | 80 | ld e,a ; 4 81 | sbc hl,de ; 15 82 | jr c,sq1 ;\ 83 | or 8 ; | branch 1: 23cc 84 | db 254 ; | <-- start of `cp *` which is 7cc to skip the next byte. 85 | sq1: ; | branch 2: 21cc 86 | add hl,de ;/ 87 | 88 | xor 6 ; 7 89 | srl d ; 8 90 | rra ; 4 91 | 92 | ; ---------- 93 | 94 | ld e,a ; 4 95 | sbc hl,de ; 15 96 | sbc a,255 ; 7 97 | srl d ; 8 98 | rra ; 4 99 | --------------------------------------------------------------------------------