ブロック転送
目次
さらに展開
2014.8.6午前追記
改良版が投稿されました。40バイトの制限の中で、もう一段ループを展開し、ループ内の転送を5回にしたものです。
転送量 512 バイトは、5 では割り切れません。そこで、3バイト重複転送することにすれば、515 / 3 = 103 で、103回ループで転送できることになります。
LDX #$66 ; 2
LOOP:
LDA $400,X ; 4
STA $600,X ; 5
LDA $467,X ; 4
STA $667,X ; 5
LDA $4CB,X ; 4 / 5
STA $6CB,X ; 5
LDA $532,X ; 4
STA $732,X ; 5
LDA $599,X ; 4
STA $799,X ; 5
DEX ; 2
BPL LOOP ; 3 / 2
冒頭で LDX #$66 としていますが、16進数の 66 は 10進数で 102 。0の時も含め、103回ループです。
さて、重複する転送個所は、LDA $4CB,X となっているところ。CB / CC / CD の3か所が重複します。
そして、この命令の実行クロック数は、4 / 5 となっています。6502 のインデックスレジスタ相対アドレッシングでは、アドレスの上位バイトに繰上りが生じた時、1クロック遅くなることがあるのです。(STA はもともと遅い代わり、この遅延は発生しません)
繰上りが問題なので、ここに重複を持ってくることで、繰り上がりにくい「前の方の」アドレスを指定しています。
さて、ループ内は、遅延が無いものとして 50クロック。103 回ループなので 5150 クロックですが、最後はループ冒頭へのジャンプが発生しないので1クロック速くなり、5149クロック。
100(hex)-cb(hex) = 53(dec) なので、53 回は遅延が発生しません。ということは、50回の遅延が発生し、50クロック遅くなります。
さらに、前処理の 2 クロックを足すと、総計は 5201 クロックです。
実は、4回のループ展開版を作成した際に、一部が重複しても良い、という考えで「6回展開」は作ろうとしました。ただ、これは41バイトになってしまうので、今回のルールでは規定違反です。
6回だと、512/6 を 256/3 2つに分けられるため、遅延の発生を抑えられます。プログラムは示しませんが、この方法だとクロック数は 5075 クロックになります。(それでも MSX には負けています)
無駄の削除
2014.8.10午前追記
ループ途中へ飛び込んで、3バイトの重複を無くすプログラムが投稿されました。
無駄があるなら省けば速い。なるほど、道理です。
LDX #$66 ;2
BNE SKIP ;3
LOOP:
LDA $400,X ;4
STA $600,X ;5
LDA $466,X ;4
STA $666,X ;5
LDA $4CC,X ;4/5
STA $6CC,X ;5
SKIP:
LDA $532,X ;4
STA $732,X ;5
LDA $599,X ;4
STA $799,X ;5
DEX ;2
BPL LOOP ;3/2
512 バイト転送を5回のループ展開で行うので、2バイトの端数がでます。ならばループの途中から初めて2バイトだけ先に転送してしまえばよい。
ループ前半は通る回数が1回減りますので、転送アドレスが調整されています。
前処理に 5クロック。ループ内に飛び込む最初の処理が 23クロック。
以降は改良前と同じ条件(遅延回数も同じ)ですが、ループ回数は1回減って 102回。
5+23+(50*102)+50 -1 = 5177
総計 5177 クロックです。
速度比較
2014.8.10 午前時点での比較です。
6502 は 5177 クロック。2倍すると 10354 クロックで、これが MSX のクロック相当での実時間になります。
Z80 は 9953 クロックなので、MSX の勝ちです。
クロック差 401クロック…4% の速度差ですね。これも、実質的には同程度の速度、と見てよいかと思います。