@weather 晴
@title Z80で1〜100を足すプログラム
@category プログラム
@s
「Z80 vs 6502」の「掛け算 - Z80 - HL活用」。「n*(n+1)/2」を求めるという話。
@s
まずオリジナル。
@qcode
LD DE,0 ; 10(11)
LD A,100 ; 7(8)
LD HL,101 ; 10(11)
OR A ; 4(5)
JP NZ,START ; 10(11)
EX DE,HL ; 4(5)
JR END ; 12(13)
LOOP1:
EX DE,HL ; 4(5)
LOOP2:
ADD HL,HL ; 11(12)
START:
RRA ; 4(5)
JR NC,LOOP2 ; 12/7(13/8)
EX DE,HL ; 4(5)
ADD HL,DE ; 11(12)
JP NZ,LOOP1 ; 10/10(11/11)
END:
SRL H ; 8(10)
RR L ; 8(10)
@s
この方法、n=100だと343clkだが、n=254だと46+(58*7+30*1)-17+20 = 485clkで、がっくり効率が落ちる。nが小さいととても効率がいいのだけど。
@s
対して、当方のコード。上の手法だと「下の位から足し上げている」のに対して、こちらは「上の位から足し上げている」。桁を送るのに「EX DE,HL」しなくていい分だけ速い。
@qcode
LD A,100 ; 7(8)
LD HL,0 ; 10(11)
LD DE,101 ; 10(11)
LD B,7 ; 7(8)
; = 38
LOOP:
ADD A,A ; 4(5)
JR NC,P1 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 18/25
P1:
ADD HL,HL ; 11(12)
DJNZ LOOP ; 13/8(14/9)
; = 26/21
JP P,END ; 10(11)
ADD HL,DE ; 11(12)
; = 11/23 = 11
END:
SRL H ; 8/10
RR L ; 8/10
; = 20
; TOTAL = 38+(25*3+18*4)+(26*6+21)+11+20 = 393
@s
ただし、nが小さくてもループ回数を減らせないため、n=100では393clkで及ばない。n=254だと38+(25*7+18*1)+(26*6+21)+11+20 = 439clkで、やっと上回る。
@s
そして、ループ展開版。
@qcode
LD A,100 ; 7(8)
LD HL,0 ; 10(11)
LD DE,101 ; 10(11)
; = 30
P0:
ADD A,A ; 4(5)
JR NC,P1 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 18/25 = 18
P1:
ADD HL,HL ; 11(12)
ADD A,A ; 4(5)
JR NC,P2 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 30/37 = 37
P2:
ADD HL,HL ; 11(12)
ADD A,A ; 4(5)
JR NC,P3 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 30/37 = 37
P3:
ADD HL,HL ; 11(12)
ADD A,A ; 4(5)
JR NC,P4 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 30/37 = 30
P4:
ADD HL,HL ; 11(12)
ADD A,A ; 4(5)
JR NC,P5 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 30/37 = 30
P5:
ADD HL,HL ; 11(12)
ADD A,A ; 4(5)
JR NC,P6 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 30/37 = 37
P6:
ADD HL,HL ; 11(12)
ADD A,A ; 4(5)
JR NC,P7 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 30/37 = 30
P7:
ADD HL,HL ; 11(12)
JP P,P8 ; 10(11)
ADD HL,DE ; 11(12)
; = 23/35 = 23
P8:
SRL H ; 8(10)
RR L ; 8(10)
; = 20
; TOTAL = 30+18+37+37+30+30+37+30+23+20 = 292
@s
ループ展開した場合、n=100で292clk、n=254だと30+25+(37*6)+23+20 = 320clk。
@s
ただし、この手法だとnが小さい……具体的にはn<16くらい……のときに効率がよくない。頭のほうで「nが十分に小さければ前半を端折る」などの処理を入れることである程度改善できる。
@qcode
LD A,100 ; 7(8)
LD HL,0 ; 10(11)
LD DE,101 ; 10(11)
LD B,2 ; 7(8)
; = 38
CP 16 ; 7(8)
JP NC,P0 ; 10(11)
ADD A,A ; 4(5)
ADD A,A ; 4(5)
ADD A,A ; 4(5)
ADD A,A ; 4(5)
DEC B ; 4(5)
; = 19/44 = 19
LOOP:
ADD HL,HL ; 11(12)
; = 12
P0:
ADD A,A ; 4(5)
JR NC,P1 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 18/25
P1:
ADD HL,HL ; 11(12)
ADD A,A ; 4(5)
JR NC,P2 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 30/37
P2:
ADD HL,HL ; 11(12)
ADD A,A ; 4(5)
JR NC,P3 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 30/37
P3:
ADD HL,HL ; 11(12)
ADD A,A ; 4(5)
JR NC,P4 ; 12/7(13/8)
ADD HL,DE ; 11(12)
; = 30/37
P4:
DJNZ LOOP ; 13/8(14/9)
; = 13/8
SRL H ; 8(10)
RR L ; 8(10)
; = 20
; TOTAL = 38+19+ 12+18+37+37+30+ 13 + 12+18+37+30+30 + 8 + 20 = 359
@s
この場合、n=100で359clk、n=254で387clk、n=15のとき263clkでまあまあな結果となる。DJNZをループ展開すればもうちょっとクロックは稼げるが、もはや意味のない領域だろう。
@s
ただそもそも、「8bit×8bit=16bit」を速くするためにこのコードを使うのは、コードサイズから考えるとあんまり効率よくないと思うんだ……(苦笑)。