ConvertValuesメソッドの解説
機械語は1バイトごとに読み込みます。ですから、ダブルワード(32ビット)の値を得るには4回読み取った後、リトルエンディアン方式に基づいてそれらを逆にし、最後に「4個の1バイトのビット列」を「32ビットの1つの値」へ変換しなければなりません。その最後の処理を実装しているのがConvertValuesメソッドです。ConvertValuesメソッドの実装は次のとおりです。
'バイト列をダブルワードの値へ変換します Private Function ConvertValues(ByVal values() As Byte) As Integer Dim result As UInteger = (CUInt(values(0)) << 24) result += (CUInt(values(1)) << 16) result += (CUInt(values(2)) << 8) result += values(3) Return result End Function
このメソッドのポイントは「算術左シフト」と「型変換」です。
算術左シフトとは、2進数表記でビットを左へずらし、開いた右の部分を0で埋める算術のことです(※厳密には他の方式もありますが、本稿ではこの形式だけを使います)。
始めて聞く人も多いと思いますので例を挙げます。
例えば、2を2進数で表記すると、2ごとに桁上がりするので「10」です。それを左にずらすということは「100」になります。100ということは10進数に直すと「2 * 2 = 4」です。このように、算術左シフトをすると値は2倍になります。
2進数の 10 …… 10進数で 2x1 + 1x0 = 2 2進数の 100 …… 10進数で 4x1 + 2x0 + 1x0 = 4
ではなぜ算術左シフトをしなくてはならないかと言うと、1バイトの値をそのまま足しても元の数に戻らないからです。これもイメージが沸きにくいので例を挙げて説明します。
例えば、8280(16進表記で2058)は、「20」(10進で32)と「58」(10進で88)に分解されてCPUオブジェクトに送られてきます。これをそのまま足してしまうと78(16進)となってしまい120(10進)となってしまいます。ですから、まず20を8ビット左算術演算して8192(32*2の8乗)にしてから、最後の88を足すのです。これでもとの値8280に復元できます。
型変換をする理由はビット数を合わせるためです。1バイト配列の変数valuesはそのまま算術左シフトしてもVBが8ビットしかないと思っているので切り捨てられます。だから32ビットであることを明示しなくてはならないのです。
バイナリプログラミングではシフト演算と桁を合わせることが大変重要です。これを覚えておけば一般の業務でも役に立ちますのでしっかりマスターしましょう。
これでAnalyzeBinaryメソッドの主な処理の解説が終わりました。これらの解説を踏まえて、もう一度AnalyzeBinaryメソッドを確認してみてください。初心者の方もきっと理解できるはずです。その際、Windows付属の関数電卓を使用していろいろな値を計算してみると理解が進むと思います。
命令の実行
いよいよ機械語の命令を実行する部分へたどり着きました。このCPUの動作はExecuteCommandメソッドで実装されています。
'解析済みの命令を実行します。 Public Sub ExecuteCommand() Dim cmd As OpeCode For Each cmd In cmds Select Case cmd.Name '命令を選択して実行 End Select Next End Sub
とても簡単です。解析されて処理待ちになっている命令に対応するメソッドを実行するだけです。今は何の命令も実装されていませんが、次回から具体的な命令を実装していきます。
次に、残りのOpeCode構造体を解説します。
OpeCode構造体
OpeCode構造体は次のように定義しています。
Public Structure OpeCode Private m_cmd As CommandName Public Property Name() As CommandName Get Return Me.m_cmd End Get Set(ByVal value As CommandName) Me.m_cmd = value End Set End Property Private m_reg As RegisterName Public Property Destination() As RegisterName Get Return Me.m_reg End Get Set(ByVal value As RegisterName) Me.m_reg = value End Set End Property '現在どのビット数の値を扱っているのかわかった方が便利 Private m_bit As Byte Public Property BitCount() As Byte Get Return m_bit End Get Set(ByVal value As Byte) m_bit = value End Set End Property '8ビットの場合、上位アドレスなのかの情報がいる Private m_hi As Boolean Public Property IsHi() Get Return Me.m_hi End Get Set(ByVal value) Me.m_hi = True End Set End Property Private m_val As Register Public Property Value() As Register Get Return Me.m_val End Get Set(ByVal value As Register) Me.m_val = value End Set End Property End Structure
今回はまだ命令を実装する段階ではないので必要最低限のものだけを用意しました。気づいた方もいると思いますが、この構造体はRegisterValueChangedEventArgsクラスとほぼ同じです。ですからこの構造体の内容に関しては、『VB.NETで仮想CPUを作ろう (4) - テストドライバの改良』のRegisterValueChangedEventArgsクラスの説明を参照してください。
これでCPUの基本動作は実装しましたが、そのままでは非常にIntelCpuクラスが扱いにくいので、いくつかの便利なメソッドを追加します。
