MC68000エミュレータの開発 その2

f:id:PocketGriffon:20211212111742j:plain

なんとなく大々的に始めてしまったMC68000エミュレータの作成。

Twitterに投稿をしたらたくさんリツイートされてしまって焦る…(-_-;;

見てる人は少ないだろうから、身内だけでこっそり進めたらいいや…という大前提が崩れてしまったかも!←じゃあ書くなって…(-_-;;

 

ちょっぴり責任感じつつ慎重に進めていくよ。

なお、作業スピードは遅めでいくと思うので、気長にお付き合いくださいませ(^^)

 

まずは事前調査

戦略において敵(敵!?)を知ることはとても大切な事だと思う。

作り始める前に十分検討をしてからでないと途中でプログラムが破綻する可能性がある。そのくらいエミュレータ作りは面倒だ!(T_T)

エミュレータ作りに必要なのは根気だと思う。

私に一番似合わない言葉だ!!!(TOT)

 

まずは手元にある、ありったけの資料をかき集める。

f:id:PocketGriffon:20211212132717j:plain

…と意気込んでみたけど、68000CPUの資料は多く持ってない事に気がついた! X68000の資料はたくさんあるんだけどなー(T-T)

過去にX68000でプログラミングをした際には、青本(68000プログラマーズ・ハンドブック)のみで事足りてしまった。この本はとても良いと思う!(^^)

 

資料を沢山用意する理由は「こっちの本に書いてある事とあっちの本に書いてある事」が違ってる場合が多いのだ!誤植の可能性もあるし単なる勘違いもありそう。そういう場合はいくつかの本を見ながら多数決で決めたりする(^^;

 

「使う分にはこれで十分な情報なんだけど、作る分には足りないんだよ!」って事も往々にある。 手元に実機があるのならばデバッガを立ち上げて実際に動かしてみるのが良い。

68000ではそういうテストをする機会がとても多い!

 

資料が揃ったところで、まずは逆アセンブラを作る事を検討する。

主な目的としては2つ。

マシン語の命令フォーマットを理解することで、エミュレータ作成時の命令デコード(このバイナリが何という命令なのかを解釈する)処理の検討が出来る。これはとても大事!

 

力技でデコード出来るCPU(8080などは256個のジャンプテーブルを用意)は特に考える必要はないけど、68000のような複雑な命令フォーマットを持つCPUでは必須だと思う。

 

2つめは、エミュレータを作り始めたら、必ず逆アセンブラが必要になるからだ。実装しようとしている命令が何なのかをバイナリで見ていく事は困難なので、そこで逆アセンブラが大活躍をする。

個人的には、エミュレータと逆アセンブラはセットで開発が必要だと思う。

 

まずは68000の命令形態について調べてみる。

f:id:PocketGriffon:20211212125043j:plain

いつもの流れだけど、私はこんな感じで書き出すようにしている。

見ても理解出来ない覚えられないの脳みそなので、自動車学校の先生に言われた「読んだって覚えん!書け!書いて覚えろ!」をいまだに真に受けて、ちゃんと書いている(^^)

 

f:id:PocketGriffon:20211212131256j:plain

まだまだ続くけど、全命令分でこれをやってみるわけだ(^^)

そうすると途中から共通点が見えてきて、脳内で命令デコードのプログラムが組み立てられていく。

 

こうして見ていくと、68000のエレガントな部分と、そうでない部分が見えてくる。

エレガントな部分はプログラムを実装する時にもキレイに書く事が出来るケースが多いが、そうでない部分は「そうでないと意識した」上で、対応が必要となる。

 

例えば一例を上げてみるとコレ。

f:id:PocketGriffon:20211212134733j:plain

これはmove命令の機械語フォーマットだ。

これによると、↓サイズの指定はこうなってる

f:id:PocketGriffon:20211212134806j:plain

2ビット(4種類)ある中で「無効、バイト、ロングワード、ワード」となっている。

 

これと比較するカタチで今度はこちら。

こっちはadd命令の機械語フォーマット。

f:id:PocketGriffon:20211212135100j:plain

これのサイズ指定は……↓

f:id:PocketGriffon:20211212135131j:plain

なんとサイズ指定の互換性がない!!

「バイト、ワード、ロングワード」という言葉だけでこの図を見ていると気づけない(T-T)

 

もっともコレは「OPモード」の指定であり、サイズ指定でないので別物だ、というのがモトローラ側の見解なんだろうけれど、作る側からみたらサイズはサイズだ(T-T)

 

こんな感じで普段アセンブラで68000コードを書いていると気が付かないような違いが見えてくる。これらを見落としていると、あとで実装が厄介だ。

 

バイトオーダー問題

普段からIntel系やARM系のCPUを使っていると、68000CPUで気になるのがバイトオーダー問題。いわゆるエンディアンというヤツだ。

「それなに?」という方は検索してもらうとして、一言でいえば「データを格納する順番の違い」かな…。

 

アセンブラを作る上ではそこまで問題にはならないんだけど、エミュレータを作る時にどう扱うのかを検討しておいた方が良さそうだ…。

 

普通に考えたら「メモリ中のデータはそのまま、メモリからレジスタへロードするタイミングでデータをひっくり返す、ストア時も同様」とするのかな。6809の実装ではそうした。

その場合、データのロードストアは頻繁に発生するので、高速に変換が出来るのかを調べておく必要がある。もしもひっくり返すのに大きなコストが必要だった場合には、最悪アプローチの変更だって検討しないといけない。

 

C言語でバイトオーダーをひっくり返すコードを書いて、アセンブラコードを確認する。

f:id:PocketGriffon:20211212151352j:plain

なるほど、どうやらARMプロセッサにはrev/rev16というバイトオーダーをひっくり返す命令が存在するようだ!これなら低コストで行けそうだ(^-^)
こんな感じで、不安要素を1つずつ減らしていくのが大事。

 

やはり詳しく書いていくと長くなるね…(^^;;

次回は逆アセンブラを作った時の事を書こうかなーと思う。

 

ちなみに現状の進捗は、X68000のリセット時から2006ステップ実行、命令グループは約13個を実装したが、必要な部分のみの実装となっているので、MC68000CPU単体で見ても1〜2%程度の完成度だろう(T-T)

 

ではまた次回!(^-^)ノ