Z80 vs 6502
目次
1~100を足すプログラム
さて、改めて、紹介されたプログラムから現状一番速いものを紹介します。
・ローカルルール
ループ展開するプログラムが現れたため、「ループ展開する際は、全体を規定バイト数に収める」ことにしたいと思います。
現状、一番長い 6502 プログラムが 28バイトで、Z80 側は 28バイト以内でループ展開を行っているため。
ただ、元々 6502 は命令が少なく、命令を組み合わせて多彩な表現を行えるように設計されていますので、同じバイト数では 6502 に不利な条件になります。
そこで、Z80 は 30バイト、6502 は 40バイトを制限としたいと思います。
これは、プログラムサイズの制限ではありません。ループ展開を制限するためのものですので、展開しなければこれ以上のサイズのプログラムでも構いません。
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クロック。