ブロック転送
1~100 を足すプログラムで、先に改良が進んだのは 6502 でした。
一時期 6502 の方が速そうに思えたので、「Z80 に花を持たせようと 16bit 演算&ループを使う題材にしたのだけど」と書いたところ、「Z80 に花を持たせるなら、ブロック転送はどうでしょう」という意見がありました。
目次
1~100の足し算(別ページ)
掛け算(別ページ)
概要
ブロック転送か…ちょっとずるい気もしますが、実用的な例題でもあります。
ブロック転送とは、連続したメモリ内容を、別のアドレスにコピーすることを言います。
別記事に書きましたが、ゲームを作る際には「画面表示タイミング」を気にしながら画面を書き変える必要がありました。
実際の VRAM と同じサイズのメモリを用意しておき、そこを仮想画面としてゲームの画面を作っておきます。
そして、画面が垂直帰線期間(何も表示しない時間)になった時に、そのメモリ内容を VRAM に転送するのです。
ファミコンでは、スプライトの設定がちょうど 256byte に収まる設計だったため、256バイトを一気に転送したりしました。
6502 は 256 バイト以上のメモリを一括して扱うのが苦手なのですが、ファミコンのハードはうまく設計されていて、256バイトの転送で済むようにしてあるのですね。
MSX では Z80 から直接 VRAM を操作することはできなかったのですが、例題ですので
・400H から 200H byte を、600H にブロック転送する
を考えてみます。
改良案は、ページ下部のコメント欄や、メールで送って下さい。待っています。(SPAM対策アドレス表記使用)
Z80基本
Z80ではこんな感じ。
LD HL,#400H ; 10 (11)
LD BC,#200H ; 10 (11)
LD DE,#600H ; 10 (11)
LDIR ; 21 / 16 ( 23 / 18)
LDIR は、ブロック転送命令です。HL から BC バイトを DE に転送します。
1バイト転送ごとに、 21 (23) クロックかかりますが、最後の1バイトだけは 16 (18) バイトで終わります。
速度は、前処理が 33クロック、LDIR が…1命令で 511*23 + 18 = 11771クロック、合計 11804 クロックですね。
これは、おそらく改良の余地のないプログラム。
(実質的に LDIR 命令1つで終わるプログラムで、他の部分はその前処理にすぎないため)
6502基本
6502ではこうです。
LDX #0 ; 2
LOOP:
LDA 400H,X ; 4
STA 600H,X ; 5
LDA 500H,X ; 4
STA 700H,X ; 5
DEX ; 2
BNE LOOP ; 3 / 2
6502 にはブロック転送命令もないですし、256 を超えるアドレスの扱いも苦手です。
そこで、512バイトを 256バイト 2つにわけて、ループの中で同時に転送しています。
転送方法も、A レジスタに読み出して、改めて別の個所に書き込むだけ。地味な作業です。
ループ内が 23 クロックで、最後だけ1クロック少ないです。ループ回数は 256回。前処理は2クロックです。
2 + 23*256 - 1 = 5889 クロック。
速度比較
6502 は 5889 クロック。2倍すると 11778 クロックで、これが MSX のクロック相当での実時間になります。
Z80 は11804 クロックなので、僅差でファミコンの勝ちです。
ただ、これも実質的には「同程度の速度」と言ってよいと思います。転送量が 256 の倍数、という 6502 に扱いやすい題材だったので高速化できたけど、そうでなければ 6502 のプログラムはややこしいことになって、MSX に負けるでしょう。
たぶん Z80 をこれ以上高速化する方法は無いと思います。ほぼ1命令で終わっているから。