2014年09月06日の日記です



続・6502は遅かったのか?  2014-09-06 10:46:38  コンピュータ

しばらく前にファミコンはMSXの半分のクロック周波数だったから MSX より遅かった、という話にたいして、そんなことは無いという記事を書きました。


元の話(ファミコンは遅かった)を書いていたページを改めて探したけど、見当たりません。

何かを調査している最中に、2~3か所見たので誤解が広まっているのだな、と思ったのだけど。


で、先の記事では比較できる命令単位で比べるとほぼ同等、と言うことを示し、実際の命令組み合わせ例でもそれほど変わらない、と示していました。




でも、組み合わせ例はあまり出来が良くなかった。

Z80 に詳しい方からも、6502 に詳しい方からも「もっと速くできる」と多数の指摘を頂きました。


これがあまりに面白かったので、調子に乗って「Z80 vs 6502」なんてページを作ってまとめてみました。


例題としては「1~100を足し合わせる」だったのですが、「その答えを知りたいなら n*(n+1)/2 の公式を使った方が速い」という意見も。


まぁ、確かにそうなのだけどそれはアルゴリズム改良であって、同じアルゴリズムで別 CPU の速度を比較する、と言う話ではなくなってしまいます。



でも、実際別アルゴリズムでの比較もやってみたい。ということで、掛け算を使った比較例もページを作りました。


作りましたっていうと偉そうだけど、僕がしたのは、教科書的なプログラムを示しただけ。

後は多くの方が「改良プログラム」を投稿してくれました。


多分こういうの来るだろうなぁ、と思っていて来るのは予想の範囲内。

でも、予想を超える方法で高速化したプログラムも数多く送られてきました。



実のところ、僕は 6502 でアセンブラを覚えましたが、当時はハンドアセンブルだったし、参考文献もそれほどなく今思えば無駄だらけのプログラムを組んでいただけ。

Z80 も少しはやりましたが、こちらもそれほど使い込んでいません。


ちゃんとアセンブラ使い始めたのは 68000 で、その後 V60 を仕事で使い込みました。

でも、仕事ではその後Cプログラムに移行していき、Cが生成した SH2 の機械語をデバッガで追う必要があったので SH2 を「読めるようにはなった」程度が最後。



だから、8bit では定番テクニックだった掛け算の高速化とか、それほど知りませんでした。

送られてきたプログラムの中には、おそらく定番の技法もあったのでしょうが、定番であっても僕にとっては「初めて見る」超絶技巧でした。




最初に書いた日記記事では、個別の命令比較では 6502 の方が速い、と言う結果でした。

でも、6502 は高速な CPU で、Z80 は高機能な CPU 。本当は単純な比較なんてできません。


命令単位で見ると 6502 の方が速そうに見えてしまうけど、Z80 の高機能を活かせば速度は同等になる、と示すのが当初の目的だったため、例題は Z80 が有利になるように意識して作られていました。



3番目の例題、ブロック転送がそのもっともたる例。


Z80 には「ブロック転送」と言う命令があり、メモリ内容を別のメモリに転送できました。

でも、6502 にはそんな命令は無く、メモリを読み出して、書き込んでを繰り返す必要があります。

6502 には絶対勝てない題材…なのですが、予想に反し 6502 は健闘。遅いとはいっても、ほぼ互角と言える速度でした。



とはいえ、最初の3題はいずれも MSX の勝ちで、これだと「MSX は速かった」という誤解をさらに広めてしまう不本意な結果になりかねません。


そこで、ファミコンが有利な例題を考えようとしました。


Z80 はメモリアクセスが苦手です。先に書いたブロック転送のように「連続メモリ」ならともかく、ランダムアクセスは本当に苦手。


それに対し、6502 はランダムアクセスはお手の物です。特に、256byte 以内のランダムアクセスは超高速。

ただ、それだけだとつまらないので、6502 にも不利な点を作るために 256byte を越える VRAM アクセスを想定した例題を作りました。



この改良は面白かった。Z80 では、スタックポインタを一時的に書き変えて高速にメモリアクセスする、なんていう手法まで飛び出しました。


でも、予想通りファミコンの勝ち。


さらに、この例題を考案中に「Z80 は条件ジャンプが遅い」という発言を見たため、条件をあらかじめ限定しにくい「ユーザー操作」を想定した、条件判断だらけの例題を出してみました。


パッド入力ユーザー操作コマンド入力の、連続した3題だったのですが…


これ、元は1つの例題として考案していたのを、プログラムしてみたら長すぎたので3分割したものです。

いたるところメモリアクセスと条件判断だらけで、Z80 が苦手そうなプログラム。



「1/60 秒に1回のユーザー操作の高速化にどれほどの意味があるのか」と言う批判もありました。

でも、例題としてはパッド入力としましたが、実際にゲームを作るときにはメモリアクセスだらけなのも、条件判断だらけなのも当然です。


そういう部分での性能を見るのが目的で、それを端的に示したのがユーザー操作だった、とお考えください。

決して 1/60 秒に「1回だけ」の処理を想定しているわけではないのです。



これは、想像以上に Z80 不利でした。苦手だろうとは思っていたけど、こんなに速度差が出るとは思ってなかった。




いつまでも続けられない(仕事時間も削られてしまうし、他に書きたい記事もあるし)ので、最後の締めくくりは伝統ある(?)例題にしようと思いました。


バイナリ数値を10進数にして表示する問題。


実はこれは、世界最初のハッカーたちが競い合ったプログラム例の一つです。

その時は「小さなコードを作る」勝負だったのですが、速度勝負でも面白いかと思いました。


教科書的には、10 で割って余りをスタックに入れ、最後にスタックから取り出して頭から並べます。

…割り算命令を持たない 8bit CPU で教科書的な方法は重いだろう、と思いましたが、掛け算もやったのですから、まずは割り算を例題としてみました。



掛け算は Z80 が速かったし、割り算もそうだろう…と思っていたら、予想外なことにファミコンの勝ち。

もっとも、現時点ではファミコンの方が速いアルゴリズムを使用していて、Z80 には移植されていません。


これで「割り算のベースはある」前提で、教科書的な10進変換ルーチンが作れます。



ネットで10進数値を作る話を調べると、「割り算よりも掛け算が速いので、逆数を掛ける」と言うテクニックもあります。


…8bit では掛け算も遅いし、掛け算ルーチン 16bit のしかないから十分精度のある逆数を作れ無さそう。

でも、例題の規定に「掛け算を使ってもよい」とあるのは、これを想定したもの。


で、割り算使ったらやっぱ遅い。


Z80 では、1bit づつあらかじめ BCD 化したテーブルを引いて足し合わせる、という処理も送られてきました。

でも、最終的に一番速かったのは「桁ごとに単純に引き算を繰り返す」プログラム。


実は、この例題では変換すべきデータを「8086」にしたのですが…


このデータ、「16bit なので最大5桁だけど、4桁なので左詰め処理が必要」で、「8 とか 6 が多いので、単純引き算ではループ回数が多くなり、時間がかかる」と言う想定で用意したものです。


それでも、やっぱ単純な引き算が速かった。


この勝負は、現在のところ MSX とファミコンが、全く同じ速度…1クロックの違いもない、という最後を締めくくるのにふさわしい名勝負となっています。




以上、まだ改良は続きそうですが、一区切りついたかな、と思うところで一旦まとめました。


お力を貸してくださった方、ありがとうございます。


そして、途中から私生活の方が忙しくなってしまい、投稿されても更新が遅れたり、プログラムの理解が低くて間違ったことを書いてしまったりしたことをお詫びいたします。

(怒らず訂正個所を教えてくださった方も多数。これにも感謝しています)





同じテーマの日記(最近の一覧)

コンピュータ

別年同日の日記

04年 SWHインテリア打ち合わせ

12年 サーバー設定 2012夏

15年 X68k 復活


名前 内容


戻る
トップページへ

-- share --

20002

-- follow --




- Reverse Link -