Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

VB.NETで仮想CPUを作ろう (11)
- DIV命令の実装とイベント設計について

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

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2008/11/21 14:00

ダウンロード VirtualCPU11.zip (317.2 KB)

 本稿では、VB.NETを利用し、簡単な機械語で動く仮想CPUを作ることでバイナリプログラミングを満喫します。実際の業務で直接使えるケースは少ないと思いますが、デバッグやプログラミングの地力向上に役立つでしょう。今回はDIV命令の実装を通じて、イベントの設計について学びます。

目次

はじめに

 本記事はVB.NETの初歩的な記法だけを使って、簡単な機械語で動く仮想CPUの実装法を解説します(※CPUにもいろいろありますが、この記事ではIntel社が製造しているCPUを対象とします)。その過程を通じて、初心者でもバイナリプログラミングが楽しめることと、バイナリプログラミングの魅力を伝えたいと思っています。

 今回はDIVの実装を通じて、仮想環境におけるイベントの実装法を解説します。

これまでの連載

下準備

 今回は前回の実装を拡張していきますので、あらかじめ第10回までの部分の実装を済ませておいてください。

DIV命令の説明

 DIV命令はMUL命令と同じく少し複雑な命令ですので、先ずはDIV命令の概要を説明します。DIV命令はMUL命令と多くの類似点があります。第一に命令形式が同じです。

DIV ソースオペランド

 ですから、MUL命令と同じくディスティネーションオペランドがないのでADD命令などとは異なり、結果を保存する場所が指定できません。ではどこへ結果が保存されるのかというと、これもMULと同じで、AL(1バイトの場合)かEAX(ダブルワードの場合)に固定されています。また、DIV命令もソースオペランドには今までのように即値を指定することができず、レジスタかメモリを指定せねばなりません。

 第二に、MUL命令とほぼ同じバイナリを使用します。オペコードはMUL命令と同じで、ModR/Mの指定法も同じです。唯一の違いは、ModR/Mの「レジスタ/オペコードフィールド」が違うだけです。

 最後に使用するレジスタもMUL命令と同じです。すなわち、1バイトの値を指定する場合はALとAHレジスタを使い、ダブルワード(4バイト)の値を指定する場合は、EAXとEDXレジスタを使用します。

 ここまでの説明で、MUL命令と似ているのなら実装は簡単だと思われた方もいるでしょう。しかし、この「似ている」が曲者なのです。似ていると言うことは、「どこで区別するか」が重要となってきます。しかし、プログラムにとって何かを区別するということは、大変重要かつ難しいことです。人間ならば一目瞭然のことでも機械には理解できないことなので、非常に細かい単位まで物事を深く考えて、間違えないように非常に慎重に作業をせねばなりません。しかし人間は機械とは違い、誰でも非常に似ているものは間違えやすいので、「似ている時こそ要注意」せねばなりません。でも心配ご無用です。筆者が一つ一つ丁寧に説明しますので、初心者の方も安心して読み進めて下さい。

SearchOpeCodeMapメソッドの修正

 DIV命令を判別するにはSearchOpeCodeMapメソッドを修正せねばなりません。しかし、先ほど説明したようにMUL命令とオペコードが同じバイナリ値なので、このメソッドだけでは判別する方法がありません。そこで筆者はCommandName構造体にUnKnownを追加してから、次のように実装しました。

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
    result.State = OpeCodeState.Fixed
    Select Case row
        Case 15
            Select Case col
                Case 6
                    result.Name = CommandName.UnKnown
                    result.BitCount = 8
                    result.NextInfo = InfoType.Register
                    result.State = OpeCodeState.ModRM
                Case 7
                    result.Name = CommandName.UnKnown
                    result.BitCount = 32
                    result.NextInfo = InfoType.Register
                    result.State = OpeCodeState.ModRM
                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

Public Structure OpeCode

    'オペコードの状態
    Private m_state As OpeCodeState
    Public Property State() As OpeCodeState
        Get
            Return Me.m_state
        End Get
        Set(ByVal value As OpeCodeState)
            Me.m_state = value
        End Set
    End Property

    '命令の名前
    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

    '命令長
    Private m_length As Byte
    Public Property Length() As Byte
        Get
            Return Me.m_length
        End Get
        Set(ByVal value As Byte)
            Me.m_length = value
        End Set
    End Property

    'オペコードの次の情報
    Private m_next As InfoType
    Public Property NextInfo() As InfoType
        Get
            Return Me.m_next
        End Get
        Set(ByVal value As InfoType)
            Me.m_next = value
        End Set
    End Property
End Structure

 以上のようにこのメソッドでは不明としておきます。それに加えて、この状況のように命令が不完全な場合、命令を確定するための情報をOpeCode構造体に追加しました。こうしておけば、複数のバイナリを必要とする機械語命令を実装できるようになります。

 この状態ではまだDIV命令を判別できませんので、次はGetModRMBinaryメソッドを修正します。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • インドリ(インドリ)

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

バックナンバー

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

もっと読む

All contents copyright © 2006-2017 Shoeisha Co., Ltd. All rights reserved. ver.1.5