SHOEISHA iD

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

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

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

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

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

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

MUL命令の実装

 MUL命令は命令フォーマットだけではなく、動作そのものも注意するべき点がいくつかあるので、まずはそれを説明してからMUL命令の実装の解説を行います。

 MUL命令は掛け算を行う一見単純な命令ですが、CPUにとって重要なことがあります。それは「桁あふれ」です。人間は最大ビット数がないのでその気になれば、紙と鉛筆で何桁でも計算できますが、CPUは数値を記憶できる範囲が限られています。分かりにくいと思いますので実例を挙げます。

 例えば、ALの値が255の時、BL(値は2)をMUL命令で計算したらCPUは困ってしまいます。なぜかと言うと、ALは1バイトまでの数値(0~255)しか記憶できないので510をALに格納できないのです。ですからMUL命令は、桁あふれが起こった場合、違うレジスタに値を格納することになっています。先ほどの例で言うと、510は2進数で111111110なので、下位の8ビット11111110をALに、余った1ビット1をAHに代入することになっています。従って先ほどの答えはAL=254、AH=1となります。なお、ダブルワードのレジスタの計算結果が桁あふれした場合は、下位32ビットをEAXに、残りの上位ビットをEDXに格納することになっています。この動作を念頭にMUL命令の実装をしてください。筆者の実装例を掲載します。

MUL命令の実装
'MUL命令の実装
Private Sub Mul(ByVal ope As OpeCode)
    Select Case ope.BitCount
        Case 8
            '掛け算をする
            Dim value As UShort = Me.AL
            If ope.IsHi Then
                value *= ope.Value.ValueHighByte
            Else
                value *= ope.Value.ValueLowByte
            End If
            '結果をレジスタに反映する
            If value <= Byte.MaxValue Then
                Me.CF = False
                Me.SF = False
                Me.AL = value
            Else '上半分をAHに下半分をALに代入する
                Me.CF = True
                Me.SF = True
                Me.AX = value
            End If
        Case 32
            '掛け算をする
            Dim value As ULong = CULng(Me.EAX) * CULng(ope.Value.ValueU32)
            '結果をレジスタに反映する
            If value <= UInteger.MaxValue Then
                Me.CF = False
                Me.SF = False
                Me.EAX = value
            Else '上半分をEAXに下半分をEDXに代入する
                Me.CF = True
                Me.SF = True
                '下半分
                Me.EAX = value And CULng(UInteger.MaxValue)
                '上半分
                Dim msk As ULong = ULong.MaxValue - CULng(UInteger.MaxValue)
                value = value And msk
                Me.EDX = CUInt(value >> 32)
            End If
    End Select
End Sub

 一番難しいと思われる「上半分をEAXに下半分をEDXに代入する」の部分を解説します。

 先ほど説明したように計算結果が32ビットを超えると、下位32ビットをEAXに、上位ビットをEDXに設定せねばなりません。それを実現するためには、ビットごとの論理積を使います。下位32ビットを取り出すにはUInteger.MaxValueと結果でビットごとの論理積を行います。上位ビットを取り出すには、結果を64ビットとして(32ビット*32ビットは最大64ビットなため)扱い、上位32ビットが1・下位32ビットが0の値とビットごとの論理積を計算します。

 ここで注意しなくてはならないのは、この時点での結果は64ビットだということです。ですから最後に32ビット右算術シフトをして32ビットの値へと変換しています。この処理が分かれば、他の処理も分かると思います。

 これでMUL命令の実装ができましたので、MUL命令を実行するためにExecuteCommandメソッドの修正を行います。

ExecuteCommandメソッドの修正

 ExecuteCommandメソッドの修正はとても簡単なため、実装のみを掲載します。

修正後のExecuteCommandメソッド
'解析済みの命令を実行します。
Public Sub ExecuteCommand()
    Dim cmd As OpeCode
    While cmds.Count <> 0
        cmd = cmds.Dequeue()
        Select Case cmd.Name
            '命令を選択して実行
            Case CommandName.Add
                Add(cmd)
            Case CommandName.Subtract
                Subtract(cmd)
            Case CommandName.Inc

                Inc(cmd)
            Case CommandName.Dec
                Dec(cmd)
            Case CommandName.Mov
                Mov(cmd)
            Case CommandName.Mul 'この部分を追加
                Mul(cmd)
        End Select
    End While
End Sub

 これでIntelCpu側ですべきことは全て終わりましたので、次項ではテストドライバ側の修正を解説します。

テストドライバ側の修正

 まず始めにすることはMUL命令が選択できるようにCmdComboコントロールのItemsプロパティの値にMulを追加することです。追加し終わったら、CmdExeButton_Clickメソッドを修正します。これから筆者の実装を掲載しますので参考にしてください。

CmdExeButton_Clickメソッド
'指定された機械語を実行する
Private Sub CmdExeButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CmdExeButton.Click
    Dim binary(1) As Byte

    '指定された命令を実行する
    If Me.GetCmmandAndRegisterBinay(binary) = False Then
        Return
    End If

    '命令長を取得
    Dim length As Integer = IntelCpu.GetCommandLength(Me.CmdCombo.SelectedIndex)

    '命令長が1の場合は値に関するバイナリは必要ない
    If length > 1 Then
        If Me.CmdCombo.SelectedIndex = CommandName.Mul Then
            'ModR/Mのバイナリを取得する
            binary(1) = IntelCpu.GetModRMBinary(CommandName.Mul, _
            CType(Me.RegisterCombo.SelectedIndex, RegisterName))
            Me.ExecuteCommand(binary)
        Else
            If Me.GetValueBinary(binary) = True Then
                Me.ExecuteCommand(binary)
            End If
        End If
    Else
        Me.ExecuteCommand(binary)
    End If
End Sub

 「ModR/Mのバイナリを取得する」の部分に注目してください。今までは単純にGetValueBinaryメソッドを呼び出して即値のバイナリを取得していましたが、MUL命令ではModeR/Mのバイナリが必要なので、GetModRMBinaryメソッドを呼び出しています。ModeR/Mは即値とは違って、必ず1バイトであることに注意してください。

 これで一応MUL命令のテストが行えますが、MUL命令が桁あふれを起こした際に混乱を生みますので、CFレジスタとSFレジスタの値が常に確認できるようにした方が良いでしょう。具体的に言いますと、CFレジスタとSFレジスタの変更イベントを作り、その通知をテストドライバに受け取り、その結果をコントロールで表示するようにします。サンプルプログラムを参考にぜひ実装してみてください。

まとめ

 いかがでしょうか? 今回はMUL命令の実装を通じて、ソースオペランドでレジスタを指定する方法を解説しました。今回で初めて、CPUにレジスタが複数あることの意味が見えてきたかと思います。次回はDIV命令を実装する予定です。この命令も手強いので読み応えがあると思います。お楽しみに。

修正履歴

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

  • 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」など、さまざまなカンファレンスを企画・運営しています。

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

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

メールバックナンバー

アクセスランキング

アクセスランキング