SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

VB.NETで学ぶ機械語の基礎

VB.NETで仮想CPUを作ろう (10)
- MLU命令の実装とModR/Mについて

VB.NETで学ぶ機械語の基礎 第10回

  • X ポスト
  • このエントリーをはてなブックマークに追加

MUL命令実装手順

 本格的にMUL命令の実装をする前に作業の手順を書きます。

  1. CommandName列挙体にMulを追加(※説明不要な作業ですので省略します)
  2. SearchOpeCodeMapメソッドの修正
  3. GetBinaryメソッドの修正
  4. AnalyzeBinaryメソッドの修正
  5. MUL命令を実装
  6. ExecuteCommandメソッドの修正
  7. テストドライバ側を修正

 次項からこの手順に沿って実装を行い、それを丁寧に説明していきます。

SearchOpeCodeMapメソッドの修正

 この段階では、中巻 B: 命令セット・リファレンス N-ZのA-6とA-7ページを参照しながら行わなければなりませんが、今回は何とMUL命令が記載されていません。アセンブラのバイナリコードを直接調べたところ、F6が1バイトのレジスタ用MUL命令、F7がワード/ダブルワード(CPUのビット数により変わります)でした。皆様もご注意ください。SearchOpeCodeMapメソッドの修正部分は下記のようになります。

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メソッドと同じですので、早速修正部分を見せます。

GetBinaryメソッド修正メソッド修正の修正箇所の抜粋
'指定された命令と対象レジスタから指定するべきバイナリを導出する
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を解析する処理を追加しなくてはなりません。筆者の実装をこれから掲載しますが、皆様も一度自分の手で実装してみてください。

AnalyzeBinaryメソッドとそれに関連するメソッド
'読み込まれたバイナリ値を解析して実行する命令を特定します。
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命令そのものの実装について説明します。

次のページ
MUL命令の実装

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
VB.NETで学ぶ機械語の基礎連載記事一覧

もっと読む

この記事の著者

インドリ(インドリ)

分析・設計・実装なんでもありのフリーエンジニア。ブログ「無差別に技術をついばむ鳥(http://indori.blog32.fc2.com/)」の作者です。アドバイザーをしたり、システム開発したり、情報処理技術を研究したりと色々しています。座右の銘は温故知新で、新旧関係なく必要だと考えたものは全て学...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/3149 2010/04/27 12:03

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング