SHOEISHA iD

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

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

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

VB.NETで仮想CPUを作ろう (9) - INC命令&DEC命令の実装と命令長

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

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

INC命令実装

 これで準備が整いましたのでINC命令を実装します。この命令は、指定したレジスタの値を1増加(インクリメント)させます。VB.NETにも同じ役割を持った演算子がありますので知っている方も多いと思います。

 この命令は一見簡単そうに見えますが、命令長が今までより少ないので十分な注意が必要です。これ以降はその点に注意しながら読み進めてください。

 始めは何時も通りCommandName列挙体にINCを追加してから、SearchOpeCodeMapメソッドにINC命令に関する処理を追加します。

SearchOpeCodeMapメソッドの修正箇所を抜粋
'行と列から命令を割り出す
Select Case row
    Case 4
        Select Case col
            Case 0
                result.Name = CommandName.Inc
                result.Destination = RegisterName.EAX
            Case 1
                result.Name = CommandName.Inc
                result.Destination = RegisterName.ECX
            Case 2
                result.Name = CommandName.Inc
                result.Destination = RegisterName.EDX
            Case 3
                result.Name = CommandName.Inc
                result.Destination = RegisterName.EBX
            Case Else
                Throw New ArgumentException("行" & row & "列" & col & "には対応しておりません。")
        End Select

 この処理については特に説明の必要がないと思いますので話しを進めます。 次はSearchOpeCodeMapメソッドの逆の処理をするGetBinaryメソッドを修正します。

GetBinaryメソッドの修正箇所を抜粋
'オペコードマップの行と列の値を決定
Select Case cmd
    Case CommandName.Inc
        result = 4 << 4
        Select Case reg
            Case RegisterName.EAX
            Case RegisterName.ECX
                result += 1
            Case RegisterName.EDX
                result += 2
            Case RegisterName.EBX
                result += 3
            Case Else
                Throw New ArgumentException("命令" & cmd.ToString() & "は" & _
                    reg.ToString() & "レジスタをサポートしておりません。")
        End Select

 このメソッドの変更点についても説明の必要がないと思いますのでさらに話しを進めます。

 次はINC命令を実行するためにIntelCPUクラスの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.Mov
                Mov(cmd)
        End Select
    End While
End Sub

 ここまでは簡単な手順でしたが次は少し難しくなります。INC命令をIncメソッドとGetIncValueメソッドの2つのメソッドとして実装します。今までは一命令一メソッドでしたが、INC命令の対応するレジスタ数が多く、同じ処理が多くなりますので、共通部分を別のメソッドとして実装します。この方がコードが読みやすくなります。

 これから筆者の実装例を掲載しますが、皆さまも一度自分で実装してみてください。ヒントはADD命令です。ADD命令の実装で分からない部分がある時は『VB.NETで仮想CPUを作ろう (7) - ADD命令実装』を一読してください。

INC命令の実装に関するソース
'Inc命令の実装
Private Sub Inc(ByVal ope As OpeCode)
    Select Case ope.Destination
        Case RegisterName.EAX
            Me.EAX = Me.GetIncValue(Me.EAX)
        Case RegisterName.EBX
            Me.EBX = Me.GetIncValue(Me.EBX)
        Case RegisterName.ECX
            Me.ECX = Me.GetIncValue(Me.ECX)
        Case RegisterName.EDX
            Me.EDX = Me.GetIncValue(Me.EDX)
    End Select
End Sub

'インクリメントして最終値を返す
Private Function GetIncValue(ByVal value As UInteger) As UInteger
    Dim tmp As ULong = value + CULng(1)
    If UInteger.MaxValue >= tmp Then
        Me.CF = False
        value = tmp
    Else '桁あふれ(オーバーフロー)が発生
        Me.CF = True
        '上位ビットを消す
        tmp = tmp And UInteger.MaxValue
        '計算結果を設定
        value = CUInt(tmp)
    End If
    Return value
End Function

 これでINC命令の実装作業は終わりです。次はDEC命令を実装します。

DEC命令実装

 この命令は、指定したレジスタの値を1減少(デクリメント)させます。VB.NETにも同じ役割を持った演算子がありますので知っている方も多いと思います。

 この命令も一見簡単そうに見えますが、減算するのでINC命令よりも処理が複雑です。『VB.NETで仮想CPUを作ろう (8) - SUB命令実装』を理解する必要があります。分からない部分が出たら参考にしてください。

 DEC命令を実装する手順はINC命令と同じなので省略し、Decメソッドに関する部分だけを掲載します。INC命令の実装を参考にぜひ皆さまも挑戦してください。どうしても分からない場合は記事に添付されているサンプルコードを参照してください。

DEC命令の実装に関するソース
'Dnc命令の実装
Private Sub Dec(ByVal ope As OpeCode)
    Select Case ope.Destination
        Case RegisterName.EAX
            Me.EAX = Me.GetDecValue(Me.EAX)
        Case RegisterName.EBX
            Me.EBX = Me.GetDecValue(Me.EBX)
        Case RegisterName.ECX
            Me.ECX = Me.GetDecValue(Me.ECX)
        Case RegisterName.EDX
            Me.EDX = Me.GetDecValue(Me.EDX)
    End Select
End Sub

'デクリメントして最終値を返す
Private Function GetDecValue(ByVal value As UInteger) As UInteger
    Dim tmp As Long = value - CLng(1)
    If 0 <= tmp Then
        Me.CF = False
        Me.SF = False
        value = tmp
    Else '桁あふれ(アンダーフロー)が発生
        Me.CF = True
        Me.SF = True
        'ビット反転させる
        Dim pmt As UInteger = CUInt(CUInt(tmp * -1) Xor UInteger.MaxValue)
        '計算結果を設定
        value = CUInt(pmt) + 1
    End If
    Return value
End Function

 後はINC命令とDEC命令の命令長が1なので、それを設定するように、GetCommandLengthメソッドを修正すればIntelCPUクラス側の準備は終わります。

INC命令とDEC命令の命令長に対応するためのコード
'命令長を取得します
Public Shared Function GetCommandLength(ByVal cmd As CommandName) As Integer
    If cmd = CommandName.Inc OrElse cmd = CommandName.Dec Then
        Return 1
    End If
    Return 2
End Function

 これでINC命令とDEC命令の両方の実装が終わりましたので、テストを行うためにテストドライバ側を変更を行います。

次のページ
テストドライバ側の対応

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

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

もっと読む

この記事の著者

インドリ(インドリ)

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

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

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

この記事をシェア

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

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング