@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」を速くするためにこのコードを使うのは、コードサイズから考えるとあんまり効率よくないと思うんだ……(苦笑)。