GetBinaryメソッドの実装
IntelCpuクラスがSearchOpeCodeMapメソッドでバイナリを解釈することを先ほど解説しました。ですがここで1つ問題があります。それは、IntelCpuクラスを利用する時、いちいちIntelマニュアルを見る必要があるということです。これでは不便なので、実行したい命令と対象レジスタを指定したら対応するバイナリが返すGetBinaryメソッドを実装します。
'指定された命令と対象レジスタから指定するべきバイナリを導出する Public Shared Function GetBinary(ByVal cmd As CommandName, _ ByVal reg As RegisterName) As Byte Dim result As Byte 'オペコードマップの行と列の値を決定 Select Case cmd Case CommandName.Mov result = 11 << 4 Select Case reg Case RegisterName.EAX, RegisterName.AX result += 8 Case RegisterName.EBX, RegisterName.BX result += 11 Case RegisterName.ECX, RegisterName.CX result += 9 Case RegisterName.EDX, RegisterName.DX result += 10 Case RegisterName.AH result += 4 Case RegisterName.AL result += 0 Case RegisterName.BH result += 7 Case RegisterName.BL result += 3 Case RegisterName.CH result += 5 Case RegisterName.CL result += 1 Case RegisterName.DH result += 6 Case RegisterName.DL result += 2 Case Else Throw New ArgumentException("命令" & cmd & "は" & _ reg & "レジスタをサポートしておりません。") End Select End Select '結果を返す Return result End Function
Mov命令の実装はまだ取り上げていませんが、何かしら命令がないと分からないのでMov命令をいち早く実装しました。このメソッドがしていることは単純です。ただ命令とレジスタに対応する数値を返しているだけです。
DivisionValueメソッドの実装
IntelCpuクラスを使用する上で次に問題になるのが、ダブルワードの値を1バイトに分解する必要があるということです。このクラスは仕様上、1バイトごとに解釈しているので、UIntger型の値を直接送るわけにはいきません。なぜならばCPUは命令も値もすべてをバイト列で解釈しているためです。ですからこのメソッドが必要となります。実装を掲載します。
'ダブルワードの値をバイト列に分割します Public Shared Function DivisionValue(ByVal value As UInteger) As Byte() Dim result(3) As Byte result(0) = (value And &HFF000000) >> 24 result(1) = (value And &HFF0000) >> 16 result(2) = (value And &HFF00) >> 8 result(3) = value And &HFF Return result End Function
このメソッドがしているのは、自分が必要のない場所を0と指定するビットごとのAnd論理積です。第3回でも説明しましたが、必要な桁を1に不必要な桁を0にしてこの演算をすれば、必要な桁の値だけが残ります。
次に算術右シフトをしています。これは算術左シフトとは逆に、一つシフトするごとに値を1/2づつ減少させます。これは、1バイトの値が必要だからです。ですから、上記のresult(0)の計算では、4バイト目の値を24ビット算術右シフトして1バイトに値を縮小しています。
実際に計算してみます。例えば2進数の「10011100 00000000」(16進数で「9C00」、10進数で「39936」)を右へ8ビットシフトすると、2進数で「10011100」(16進数で「9C」、10進数で「156」)となります。値が1/2の8乗、つまり1/256になり、1バイト分の値に縮小できていることも分かります(ここでは下位から2バイト目の値を取得)。
2進数の 10011100 00000000 2進数の 10011100 ←8ビット右シフト
この2つの算術を行うことにより、ダブルワードの値を4個のバイトにできます。この説明を読んでよくわからない方は、この記事の例の数値を変えてWindows付属の関数電卓で何度か計算してみてください。そうすればよく分かります。
まとめ
いかがだったでしょうか? 筆者としては今までで一番面白い回だと思います。実装を通じてIntelのCPU動作のイメージが少しずつ浮かびあがってきたのではないでしょうか。
ぜひ一度、自分自身でサンプルプログラムを参考にプログラミングをしてみてください。今までブラックボックスだったCPUの中身が少し見えてくるでしょう。バイナリプログラミングの魅力はそこにあります。この記事を通じて、CPUの理解の助けになれば幸いです。
次回からは機械語命令を実装していきます。お楽しみに。
