Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

VB.NETで仮想CPUを作ろう (6) - MOV命令の実装

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

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2008/09/04 15:12

本稿では、VB.NETを利用し、簡単な機械語で動く仮想CPUを作ることでバイナリプログラミングを満喫します。実際の業務で直接使えるケースは少ないと思いますが、デバッグやプログラミングの地力向上に役立つでしょう。今回は機械語の一番基礎的な命令MOVを実装することにより、CPU基本的動作の実装を進めます。

目次

はじめに

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

 前回 CPUの基礎動作を実装したので、今回は機械語の命令を実装していきます。

これまでの連載

下準備

 前回の実装を拡張していくので、あらかじめ前回までの部分の実装を済ませておいてください。後は専門用語とCPU構造の確認のため、第1回で用意した用意した3つのIntel社のマニュアルをすぐ読める状態にしてください。

 それに加えて、中巻 B: 命令セット・リファレンス N-ZのA-6とA-7ページを印刷しておくことをお勧めします。絶対に必要なことではありませんが、この表をあらかじめ印刷しておき、その紙を参照しながら実装作業をすると効率がよくなります。

MOV命令実装の大まかな流れ

 基本的に、機械語命令の動きをシミュレートするには、前段階としてCPUの基礎動作である、命令読み込み・命令解析・実行を実装する必要があります。

 ですが、前回 命令読み込み・命令解析・実行をしっかり実装したので、今回を行う作業はそれほど大変ではありません。

 バイナリから命令を判別するためのSearchOpeCodeMapメソッドと、実行部分を実装したExecuteCommandメソッドに少しの変更を加えて、最後にMOV命令そのものを実装するためにMovメソッドを追加するだけです。

SearchOpeCodeMapメソッドの変更点

 このメソッドにMOV命令に関する処理を付け加える場所は、コメントの「行と列から命令を割り出す」の真下です。分かりやすいように前回の時点での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)

    '行と列から命令を割り出す
    'ここにMOV命令に関する処理を追加


    '結果を返す
    Return result
End Function

 ここに一度自分でプログラムを追加してみましょう。

 ヒントはIA-32 インテル アーキテクチャー・ソフトウェア・デベロッパーズ・マニュアル、中巻 B: 命令セット・リファレンス N-ZのA-6とA-7ページです。自分でこの表からMOV命令を探し、その動作をプログラムで行わせるにはどうしたらいいのか一度考えてみるとよいでしょう。

 筆者の回答例を提示するので、確認してください。

'行と列から命令を割り出す
Select Case row
    Case 11
        result.Name = CommandName.Mov
        Select Case col
            Case 0
                result.Destination = RegisterName.AL
                result.BitCount = 8
            Case 1
                result.Destination = RegisterName.CL
                result.BitCount = 8
            Case 2
                result.Destination = RegisterName.DL
                result.BitCount = 8
            Case 3
                result.Destination = RegisterName.BL
                result.BitCount = 8
            Case 4
                result.Destination = RegisterName.AH
                result.BitCount = 8
                result.IsHi = True
            Case 5
                result.Destination = RegisterName.CH
                result.BitCount = 8
                result.IsHi = True
            Case 6
                result.Destination = RegisterName.DH
                result.BitCount = 8
                result.IsHi = True
            Case 7
                result.Destination = RegisterName.BH
                result.BitCount = 8
                result.IsHi = True
            Case 8
                result.Destination = RegisterName.EAX
                result.BitCount = 32
            Case 9
                result.Destination = RegisterName.ECX
                result.BitCount = 32
            Case 10
                result.Destination = RegisterName.EDX
                result.BitCount = 32
            Case 11
                result.Destination = RegisterName.EBX
                result.BitCount = 32
            Case Else
                Throw New ArgumentException("行" & row _
                    & "列" & col & "には対応しておりません。")
        End Select
    Case Else
        Throw New ArgumentException("行" & row _
            & "列" & col & "には対応しておりません。")
End Select

 正解を見て、意外と簡単だと思った人が多いでしょう。それでは解説します。

 この部分でするべきことは、行と列の2つのニブルからMOV命令がどのレジスタを対象にしているのかと、そのレジスタが何ビットなのかresult変数に設定するだけです。

 具体的には、IA-32 インテル アーキテクチャー・ソフトウェア・デベロッパーズ・マニュアル、中巻 B: 命令セット・リファレンス N-ZのA-6とA-7ページを見て、そこからMOV命令を探してください。11行目(16新表記でB)にMOV命令があります。これをプログラムで表現すると、次のようになります。

※重要な部分だけ抜粋
Select Case row
    Case 11
        result.Name = CommandName.Mov

 続いて、11行目の列から必要なレジスタ、AL・BL・CL・DL・AH・BH・CH・DHを探してください。A-6ページの0~7列で見つかるはずです。これをプログラムで表現すると、次のようになります。

※重要な部分だけ抜粋
Select Case col
    Case 0
        result.Destination = RegisterName.AL
        result.BitCount = 8

 次はA-7ページの11行目(B)を見てください。eAX・eCX・eDX・eBXとなっており、EAXなどの見慣れたレジスタが見当たりません。これはどういうことでしょうか?

 それは、Intel社のCPUが以前16ビットだったことが原因です。16ビットの頃はAX・CX・DX・BXとなっていました。しかし、現在主流の32ビットCPUを販売する頃に、過去との互換性をもたせつつ機械語を設計し直す必要が出てきたため、Intelは、AX・CX・DX・BXの場所を16ビットCPUの場合はそのまま、32ビットCPUの場合はEAX・ECX・EDX・EBXと、CPUのビット数に合わせて変化させることを決定しました。

 こうすることで、今までの16ビットCPU用機械語はそのまま動きますし、32ビットCPU用機械語は基本的に32ビットを扱うので両方の要望を満たせます。

 今回は32ビットCPUを扱うので、eAXなどのレジスタは32ビットレジスタとみなしてください。そうするとプログラムは次のようになります。

※重要な部分だけ抜粋
Select Case col
    Case 8
        result.Destination = RegisterName.EAX
        result.BitCount = 32
    Case 9
        result.Destination = RegisterName.ECX
        result.BitCount = 32
    Case 10
        result.Destination = RegisterName.EDX
        result.BitCount = 32
    Case 11
        result.Destination = RegisterName.EBX
        result.BitCount = 32

 気になる人も多いと思うので、ここではBitCountだけ説明しておきます。このプロパティはレジスタのビット数のことで、いろいろな場面で重要となるのでこの情報を保存しています。

 これでMOV命令の情報をすべて設定できるので、次は実行部分であるExecuteCommandメソッドにプログラムを追加します。


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

著者プロフィール

  • インドリ(インドリ)

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

バックナンバー

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

もっと読む

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