MUL命令実装手順
本格的にMUL命令の実装をする前に作業の手順を書きます。
- CommandName列挙体にMulを追加(※説明不要な作業ですので省略します)
- SearchOpeCodeMapメソッドの修正
- GetBinaryメソッドの修正
- AnalyzeBinaryメソッドの修正
- MUL命令を実装
- ExecuteCommandメソッドの修正
- テストドライバ側を修正
次項からこの手順に沿って実装を行い、それを丁寧に説明していきます。
SearchOpeCodeMapメソッドの修正
この段階では、中巻 B: 命令セット・リファレンス N-ZのA-6とA-7ページを参照しながら行わなければなりませんが、今回は何とMUL命令が記載されていません。アセンブラのバイナリコードを直接調べたところ、F6が1バイトのレジスタ用MUL命令、F7がワード/ダブルワード(CPUのビット数により変わります)でした。皆様もご注意ください。SearchOpeCodeMap
メソッドの修正部分は下記のようになります。
'オペコードマップを検索し、実行する命令と対象となるレジスタを探索する。 Private Shared Function SearchOpeCodeMap(ByVal binary As Byte) As OpeCode Dim result As OpeCode = New OpeCode() '指定されている命令を特定するために行と列を導出 Dim row As Byte = CByte((binary And &HF0) >> 4) Dim col As Byte = CByte(binary And &HF) '行と列から命令を割り出す result.NextInfo = InfoType.Value Select Case row Case 15 Select Case col Case 6 result.Name = CommandName.Mul result.BitCount = 8 result.NextInfo = InfoType.Register 'ここに注意 Case 7 result.Name = CommandName.Mul result.BitCount = 32 result.NextInfo = InfoType.Register 'ここに注意 Case Else Throw New ArgumentException("行" & row & "列" & col & "には対応しておりません。") End Select Case Else Throw New ArgumentException("行" & row & "列" & col & "には対応しておりません。") End Select '命令長設定 result.Length = GetCommandLength(result.Name) '結果を返す Return result End Function
今回はNextInfo
プロパティに値を設定していることに注意してください。この処理はオペコードの次にModR/MのバイナリがあることをIntelCpu
オブジェクトに示すためのものです。その他の部分についてはいつもと同じです。これでSearchOpeCodeMap
メソッドの修正は終わりです。次項ではGetBinary
メソッドの修正を行います。
GetBinaryメソッドの修正
GetBinary
メソッドの考え方はSearchOpeCodeMap
メソッドと同じですので、早速修正部分を見せます。
'指定された命令と対象レジスタから指定するべきバイナリを導出する Public Shared Function GetBinary(ByVal cmd As CommandName, ByVal reg As RegisterName) As Byte Dim result As Byte = 0 'オペコードマップの行と列の値を決定 Select Case cmd Case CommandName.Mul result = 15 << 4 If reg >= RegisterName.EAX And reg <= RegisterName.EDX Then result += 7 ElseIf reg >= RegisterName.AH And reg <= RegisterName.DL Then result += 6 Else Throw New ArgumentException("命令" & cmd.ToString() & "は" & _ reg.ToString() & "レジスタをサポートしておりません。") End If End Select '結果を返す Return result End Function
このメソッドでは、F6が1バイトのレジスタ、F7がダブルワードのレジスタなので、指定されたレジスタがどちらなのかを判断して、列の値を加算しています。これでGetBinary
メソッド修正は終わりです。次項ではAnalyzeBinary
メソッドの修正について解説します。
AnalyzeBinaryメソッドの修正
MUL命令を正常に動作させるためには、命令フォーマットを正しく解析する必要があります。前回までは即値しか解析していませんでしたので、AnalyzeBinary
メソッドにModR/Mを解析する処理を追加しなくてはなりません。筆者の実装をこれから掲載しますが、皆様も一度自分の手で実装してみてください。
'読み込まれたバイナリ値を解析して実行する命令を特定します。 Public Sub AnalyzeBinary() Dim binary As Byte While binarys.Count <> 0 '指定されている命令とレジスタを決定 binary = binarys.Dequeue() Dim ope As OpeCode = SearchOpeCodeMap(binary) '値を導出 If ope.NextInfo = InfoType.Value Then '即値をバイナリから取得 If ope.BitCount = 32 Then Dim values(3) As Byte For i As Integer = 0 To 3 values(i) = Me.binarys.Dequeue() Next 'Intelはリトルエンディアンなので値を逆にする values = GetLittleEndianValue(values) '取得したバイト配列からダブルワードの値を構築 Dim value As UInteger = ConvertValues(values) ope.Value = New Register(value) Else ope.Value = New Register(binarys.Dequeue(), ope.IsHi) End If ElseIf ope.NextInfo = InfoType.Register Then 'レジスタから値を取得 If ope.BitCount = 32 Then Dim modrm As ModRM = New ModRM(Me.binarys.Dequeue()) Dim reg As RegisterName = GetRegister32Name(modrm.RMValue) ope.Value = New Register(Me.GetRegister32Value(reg)) ElseIf ope.BitCount = 8 Then Dim modrm As ModRM = New ModRM(Me.binarys.Dequeue()) Dim reg As RegisterName = GetRegister8Name(modrm.RMValue) ope.Value = New Register(Me.GetRegisterByteValue(reg)) End If End If '実行する命令を記憶 cmds.Enqueue(ope) End While End Sub '指定したダブルワードレジスタの値を取得する Private Function GetRegister32Value(ByVal reg As RegisterName) As UInteger Select Case reg Case RegisterName.EAX Return Me.EAX Case RegisterName.EBX Return Me.EBX Case RegisterName.ECX Return Me.ECX Case RegisterName.EDX Return Me.EDX Case Else Throw New ArgumentOutOfRangeException(reg & "はダブルーワード(32ビット)レジスタではありません。") End Select End Function '指定したバイトレジスタの値を取得する Private Function GetRegisterByteValue(ByVal reg As RegisterName) As UInteger Select Case reg Case RegisterName.AH Return Me.AH Case RegisterName.AL Return Me.AL Case RegisterName.BH Return Me.BH Case RegisterName.BL Return Me.BL Case RegisterName.CH Return Me.CH Case RegisterName.CL Return Me.CL Case RegisterName.DH Return Me.DH Case RegisterName.DL Return Me.DL Case Else Throw New ArgumentOutOfRangeException(reg & "はバイト(8ビット)レジスタではありません。") End Select End Function
最初の方の処理でOpeCode
構造体のNextInfo
プロパティの値に基づいて、解析対象が即値なのかModR/Mなのかを判定しています。これにより、MUL命令の特殊なフォーマットに対応します。即値については既に解説済みなのでModR/Mの部分だけ説明をします。
難しく見えるかもしれませんがModR/Mの解析は簡単です。始めにレジスタのビット数を判定してから、その情報を元にソースオペランドとして指定されたレジスタの名前を取得します。次に指定されたレジスタの値を取得し、その値をOpeCode
構造体のValue
プロパティに設定しているだけです。これでバイナリ解析に関する修正は終わりです。次項ではついにMUL命令そのものの実装について説明します。