Z80 vs 6502
目次
6502基本
まずは素直な改良。基本的なプログラムとなります
LDA #100 ; 2
STA >2 ; 3
LDA #0 ; 2
TAX ; 2
CLC ; 2
LOOP:
ADC >2 ; 3
BCC SKIP ; 3 / 2
INX ; 2
CLC ; 2
SKIP:
DEC >2 ; 5
BNE LOOP ; 3 / 2
STA >0 ; 3
STX >1 ; 3
僕のプログラムでは、足す数は 8bit ですが、これを 16bit に拡張した形で、常に 16bit 演算していました。
元の趣旨が Z80 が有利になるように 16bit 演算をする、だったためです。
しかし、これは無駄が多いので「繰り上がりが生じた時のみ、上位バイトを計算する」ようにしたのが上のプログラムです。
上位バイトは X レジスタに入れています。X レジスタは計算用ではありませんが、インクリメント(数を1増やす)程度はできます。
繰り上がりならこれで十分です。
これにより、A レジスタを使いまわして上位バイトと下位バイトをそれぞれ計算する必要は無くなったので、Aレジスタは下位バイトの計算専用になりました。
値の出し入れのオーバーヘッドが無くなり、大幅に高速化されています。
前処理は、合計 14クロック。
メインループは、繰り上がりが発生しない場合 14 クロック。
繰り上がりが発生すると、17 クロックになります。
この計算、結果は 5050 になる、とわかっていますが、256 で割ると 19.7 。上位バイトへの繰り上がりが 19回行われる、ということです。
全体の計算回数は 100回なので、81回は繰り上がりが発生しません。
(14clock * 81 + 17clock * 19) = 1457clock
ただし、最後の1回はループのジャンプがないため、1クロック減ります。
そして、計算終了後に6クロックの後処理があります。これは、元のプログラムに合わせて値をメモリに格納するため。
合計すると、1476 クロックです。
僕の示したプログラムは 2625 クロックでしたので、ずいぶん早くなりました。
繰り上がり高速化
そして、さらなる改良。
LDA #100 ; 2
STA >2 ; 3
LDA #0 ; 2
TAX ; 2
JMP SKIP ; 3
INCH:
INX ; 2
DEC >2 ; 5
BEQ EXIT ; 3 / 2
SKIP:
CLC ; 2
LOOP:
ADC >2 ; 3
BCS INCH; 3 / 2
DEC >2 ; 5
BNE LOOP; 3 / 2
EXIT:
STA >0 ; 3
STX >1 ; 3
先のプログラムでは、「繰り上がりが発生しない」場合に、繰り上がり処理をジャンプして飛ばしていました。
しかし、6502 の条件ジャンプは、「ジャンプしない」時の方が高速に動きます。
繰り上がりは、19 回発生して、81 回発生しません。ならば、繰り上がりが発生した時にジャンプした方が全体として速い、というのが上のプログラムの着眼点。
これで、メインループで繰り上がりがない場合が 13クロックになります。(1クロック減った)
それ以外の処理は巧妙に作られており、先のプログラムと全く同じクロック数です。
結果ももちろん、繰り上がりがない回数の 81 クロック減り、総計 1395クロック。
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 クロックではまだ遅いです)