strcmpを見たついでに、ループ処理の話をしよう。まずは単純に文字列の末端を探すループを書いてみる。
while (*s) { s++; }
素直にMC6800のアセンブラに落とすとこうなるが、こう書いてはいけない。
loop: tst 0,x beq break inx bra loop break:
ループ末端は分岐命令にする
先のコードはループ1回あたり 7+4+4+4 = 19cyc 必要である。
分岐を最後に移動すると、バイト数は同じだがループ内は 4+7+4 = 15cyc に減る。
bra start loop: inx start: tst 0,x bne loop break:
先にdexする
コードをよくみると、最初の bra start は inx をスキップしているだけである。
dexしてinxすれば元に戻るので、そのように書き直す。実行時間は同じだが1バイト減る。
inx/dexが遅いんだよな、MC6800。Big Endian なので素直に(トランジスタを節約して)実装すると遅くなるのは仕方ない。
dex loop: inx tst 0,x bne loop break:
文字列の長さが0バイトの場合だけは、元バージョンは7+4=11cycで終了し、こちらは4+4+7+4=19cycかかるので遅い(最初のdex/inxの8cyc分だけ遅い)。
ループ1回あたり4cyc縮むので、2文字以上あれば高速になる。
なるべくtstは使わない
MC6800レジスタを壊さずにメモリ内容をテスト・操作する命令があって便利である。便利だが遅いのである。
上記プログラムの tst を ldaa に変えると、ループ内は 4+5+4 = 13cyc になり、2cyc減る。
AccA,Bが使えるなら、tstの代わりにldaを使うべきである。
dex loop: inx start: ldaa 0,x bne loop break:
なんでtstが遅いのか?
Motorolaのマニュアルの 命令ごとの CYCLE-BY-CYCLE 表を見るとわかる。
tst命令はaslなどのメモリ操作命令と同一タイミングになっている。たぶんトランジスタ節約のためだろう。
そのためにアドレスバスに再度アドレスを出し、書き込むサイクルが発生していて、これが2サイクル余分にかかる原因となっている。
(もちろん、tst命令では メモリを書き換えないように VMA=0 で保護しているが、実行時間は無駄になる)
Motorola MC6800 8-BIT MICROPROCESSING UNIT (MPU) p.34 TABLE-16 – INDEXED MODE CYCLE BY CYCLEを加工し作成
日立HD6803では不要なメモリサイクルが減ったので、気兼ねなくtstを使えるようになったが、モトローラ版はMC68HC11に至っても遅いままなのが困りものである。


コメント