掛け算

目次

概要

Z80基本

構造整理HL活用参考記録変数共用

6502基本

結果のレジスタ持ち計算手順逆転ループ展開変数共用分岐改良最初の処理を展開

速度比較


HL活用

2014.8.5 午前追記

Z80 は 16bit レジスタを何本か持ちますが、8bit CPU です。そのため、16bit 命令は使用に様々な制限があります。

HL 以外は計算ができない、というのもその一つ。ここまでのプログラムでは、BC を2倍するのに、C と B をそれぞれシフトしていました。

では、BC を HL にしてしまえば、 ADD HL,HL で簡単に2倍できるよ、というのが以下のプログラム。


	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)

BC の代わりに HL を使っていますが、最終結果にも「どんどん足していく」必要があるので、やはりHL を使っています。

Z80 は HL と DE の内容を瞬時に入れ替える機能(EX DE,HL)があり、これを活用しているのです。

そのため、前処理時点では、DE を 0 にしています。この時点では、DE が「結果」で、プログラム中で必要に応じて HL と入れ替わります。


前処理が 46クロック、後処理が 20 クロック。

計算ループは、加算有りが 58 クロック、加算無しが 30 クロック。最後は 17クロック速くなります。

総計は 46 + 58*3 + 30*4 -17 + 20 = 343 クロック


加算があるときはレジスタ切り替えの手間が発生するため、どうしても遅くなってしまう、というのが残念なところ。

しかし、機能の制限から必然的に使用するレジスタが決められている、という構造は美しいです。


参考記録

2014.8.8午後追記

GORRY 氏から投稿いただきました。記録更新ではないのですが、非常に面白いので参考記録として記しておきます。

考察も含めて面白いので、プログラムは氏のページを参照してください。


HL 活用プログラムは高速なのですが、実は高速なのは 乗数を 100 に特化しているから、という側面もあります。加算有りと加算無しの差が激しすぎるのね。

氏の作ったプログラムは、乗数 100 では遅いものの、一般化した際に HL 活用プログラムよりも高速になるようにしてあります。本当は、そういうのが「速いコード」と言えるのだと思う。


この企画の最初のページにもあるように、厳密な速度測定は不可能なのですね。ルールがなければ比較にならないし、ルールを作れば、そのルール「だけ」への最適化が避けられない。ここは楽しめればよいので、乗数 100 で速いコードで比較する、で良いと思っています。(ただし、他の乗数でも動かなくてはならない)


ループを8展開した版では、乗数 100 の場合でも速度を更新(292クロック)しているのですが、余りに現実的ではないコードなので、ここでは参考記録にとどめます。



以下余談。

GORRY 氏は、マイコンBASICマガジンと言う雑誌でライターをしていた方です。凄腕プログラマ。

僕は当時熱心な読者だったので、投稿いただき光栄に思っています。


Twitter でZ80に対するのは6809という認識を呟いておられました。Dr.D のマシン語寺子屋で機械語を覚えた僕としても、そう思います。

でも、今回は「ファミコンは MSX の半分の周波数で動いているから、速度も遅かった」と思っている人がいるようなので、反証したかったのが発端です。

Z80 のライバルは 6809 なのだけど、MSX のライバルは FM-7 ではなく、ファミコンだったという認識。


僕は 6502 と Z80 なら「それなりに」わかるけど、6809 はあまり知らない、というのもありますが。


変数共用

2014.8.8午後追記

後で書く(でも先に公表された)6502 の変数共用版を移植したものです。

実は、僕も移植を試みたのですが、うまくできないなぁ、と思っていました。上手な人が処理すればちゃんとできるものですね。


	LD DE,101 ; 10 (11);
	LD HL,100 *256 ; 10 (11)
	LD B,2 ; 7 (8)
LOOP:
	ADD HL,HL ; 11 (12)
	JR NC,SKIP1; 12 / 7 (13 / 8)
	ADD HL,DE ; 11 (12)
SKIP1:
	ADD HL,HL ; 11 (12)
	JR NC,SKIP2; 12 / 7 (13 / 8)
	ADD HL,DE ; 11 (12)
SKIP2:
	ADD HL,HL ; 11 (12)
	JR NC,SKIP3; 12 / 7 (13 / 8)
	ADD HL,DE ; 11 (12)
SKIP3:
	ADD HL,HL ; 11 (12)
	JR NC,SKIP4; 12 / 7 (13 / 8)
	ADD HL,DE ; 11 (12)
SKIP4:
	DJNZ LOOP ; 13 / 8 (14 / 9)
	SRL H ; 8 (10)
	RR L ; 8 (10)

D は常に 0 。E に被乗数が入っています。乗数は H に入っていて、結果は HL に入ります。

乗数が H に入っているのに結果も HL に入れられる理由は、 6502 版の説明を読んで (^^;


前処理部分、投稿では公式 n*(n+1)/2 に厳密に作られていて、100 をコピーしてから+1 してました。他のプログラムで「100 と 101」を直接入れていますので、同じように変更しました。(コードサイズは変わらず、こちらの方が速い)

ローカルルールを定める前だったので、他の例題のローカルルールに従い、30バイトで作られています。この例題だけローカルルールを変えて定めたのは、30バイトでは難しそうだと思ったからなのだけど、ちゃんと収まっている。すごい。


前処理が 30クロック。後処理が 20 クロック。

筆算1段分の計算が、加算有り 32クロック、加算無し 25クロック。ループ展開されているので、4回目に 14クロックで先頭に戻り、8回目には 9クロックで後処理に進みます。


30 + 20 + 32*3 + 25*5 + 14 + 9 = 294


総計 294クロックです。


次ページ: 6502基本


前ページ 1 2 3 4 5 6 次ページ

(ページ作成 2014-08-03)
(最終更新 2014-09-01)
第2版 …他の版 初版

前記事:Z80 vs 6502     戻る     次記事:ブロック転送
トップページへ

-- share --

5000

-- follow --




- Reverse Link -