Z80 vs 6502
目次
Z80 基本
つづいて Z80 。
6502 の改良プログラムを移植する形で、あえて 8bit 演算にして速度を稼ぐ、というプログラムもありました。
しかし、最終的には 16bit 演算をスマートに使ったプログラムが速いようです。
まず、基本系。
LD HL,0 ; 10 (11)
LD BC,100 ; 10 (11)
LOOP:
ADD HL,BC ; 11 (12)
DEC C ; 4 (5)
JP NZ,LOOP ; 10 / 10 (11 / 11)
最初に僕が作ったプログラムに似ていますが、DE レジスタを使わなくなりました。
ループも、DJNZ ではなく DEC C ; JP NZ で回しています。
DJNZ では ループ変数として暗黙に B レジスタを使うのですが、これを C レジスタに変えているわけです。
局所的には遅くなりますが、これでループ変数と「足す数」を共通に出来るため、DE レジスタの操作がいらなくなって全体として高速化しています。
LOOP より前の前処理が 22 クロック、LOOP 後が 28 クロックで、100回繰り返すので 2800 クロック。
合計、2822 クロックで計算を終わります。
僕が最初に作ったプログラムは 3325 クロックでしたから、少し早くなりました。
しかし、これだとクロック周波数(ファミコンは MSX の半分)を考慮しても、速度でファミコンに負けています。
(6502 は、この時点で 1395 クロックでした。MSX の速度に合わせると、この値を倍にして 2790 クロック。2822 クロックではまだ遅いです)
ループ展開
そこで、「6502 のプログラムは結構長い(28バイト)ので、同程度のサイズまでループ展開させてもらった」というのが次のプログラム。
LD HL,0 ; 10 (11)
LD BC,100 ; 10 (11)
LOOP:
ADD HL,BC ; 11 (12)
DEC C ; 4 (5)
JR Z,EXIT ; 12 / 7 (13 / 8)
ADD HL,BC ; 11 (12)
DEC C ; 4 (5)
JR Z,EXIT ; 12 / 7 (13 / 8)
ADD HL,BC ; 11 (12)
DEC C ; 4 (5)
JR Z,EXIT ; 12 / 7 (13 / 8)
ADD HL,BC ; 11 (12)
DEC C ; 4 (5)
JR Z,EXIT ; 12 / 7 (13 / 8)
ADD HL,BC ; 11 (12)
DEC C ; 4 (5)
JP NZ,LOOP ; 10 / 10 (11 / 11)
EXIT:
全体としては 27バイトで、6502 のプログラムよりは短いです。
基本形としては先に挙げたものと同じなのですが、見て判る通り、ループ内のプログラムを繰り返しています。
「ループ展開」というテクニックです。
今の CPU では、キャッシュにプログラムが収まっているのが最速なので、プログラムは小さいほうが速いです。
しかし Z80 の時代にはキャッシュなどないため、ループ回数を減らしてオーバーヘッドを無くす、というのが高速化手法として有効でした。
とはいえ、このプログラムでは、どこで終了条件が来るかわからないため、条件分岐を多数入れています。
ループの条件判断より高速化されるようになっていますが、このオーバーヘッドが結構大きい。
前処理が 22 クロック。
ループ内が、基本単位 25 クロックを5回繰り返しているのですが、最後だけ 28 クロックなので 25*4 + 28 = 103 クロック。
これを 25 回繰り返すので、 103 * 25 = 2575 クロック。
合計で、2582 クロック。ファミコンより速くなりました。
条件省略
…もう一ひねりしてみます。これは投稿されたものではなく、僕の方で作ったもの。(作成後、総計で3クロック減る局所的な改良案を頂き反映しています)
LD BC,100 ; 10 (11)
LD HL,0 ; 10 (11)
BIT 0,C ; 8 (10)
JR NZ,MOD1 ; 12 / 7 (13 / 8)
LOOP:
ADD HL,BC ; 11 (12)
DEC C ; 4 (5)
MOD1:
ADD HL,BC ; 11 (12)
DEC C ; 4 (5)
JP NZ,LOOP ; 10 / 10 (11 / 11)
前処理が長くなったのでループ展開が2回に減ってしまった…でも、これで 17byte 。
後で指摘され、さらに展開したバージョンが投稿されました。(この次のプログラム)
前処理の中で「端数」が出るかどうかを確認し、端数がある場合はループの途中に飛び込んでいます。
なので、ループの最後以外では終了条件の確認が不要になっています。
これも、不定回ループを展開するときの基本テクニックでした。
前処理が 40 クロック。ループ内が 45クロックで、50回繰り返しますから 45*50 = 2250 クロック。
合計で、2290 クロックです。