はじめに
本記事はVB.NETの初歩的な記法だけを使って、簡単な機械語で動く仮想CPUの実装法を解説します(※CPUにもいろいろありますが、この記事ではIntel社が製造しているCPUを対象とします)。その過程を通じて、初心者でもバイナリプログラミングが楽しめることと、バイナリプログラミングの魅力を伝えたいと思っています。前回は、仮想CPUの必須項目であるレジスタを実装しました。今回は、さらに仮想CPUの実装を進めます。
下準備
今回は前回の実装を拡張していきますので、あらかじめ前回までの部分の実装を済ませておいてください。後は専門用語とCPU構造の確認のため、第1回で用意した3つのIntel社のマニュアルをすぐ読める状態にしてください。
仮想CPUにレジスタを実装
前回は仮想CPU実装を簡単にするためにレジスタ構造体を実装しました。これで、いよいよ仮想CPUクラスを作成する準備が整ったことになります。では、早速CPUをエミュレートするクラスを作りましょう。名前はIntel製であることを考慮してIntelCpuにします。IntelCpu.vbファイルを前回作ったプロジェクトに追加してください。
Public Class IntelCpu End Class
これでCPUクラスの大まかな定義ができました。まずは実装するレジスタを確認してから、このIntelCpu
クラスにレジスタプロパティを実装します。実装するレジスタは次のとおりです。
- EAX(ダブルワード:32ビット)
- EBX(ダブルワード:32ビット)
- ECX(ダブルワード:32ビット)
- EDX(ダブルワード:32ビット)
レジスタのビット数に注意しながら、各レジスタをIntelCpu
のプロパティとして実装すると次のようになります。
Public Class IntelCpu #Region "EAXレジスタ" Private m_eax As Register Private Property EAX() As UInteger Get Return Me.m_eax.ValueU32 End Get Set(ByVal value As UInteger) Me.m_eax.ValueU32 = value End Set End Property Private Property AX() As UShort Get Return Me.m_eax.ValueU16 End Get Set(ByVal value As UShort) Me.m_eax.ValueU16 = value End Set End Property Private Property AH() As Byte Get Return Me.m_eax.ValueHighByte End Get Set(ByVal value As Byte) Me.m_eax.ValueHighByte = value End Set End Property Private Property AL() As Byte Get Return Me.m_eax.ValueLowByte End Get Set(ByVal value As Byte) Me.m_eax.ValueLowByte = value End Set End Property #End Region #Region "EBXレジスタ" Private m_ebx As Register Private Property EBX() As UInteger Get Return Me.m_ebx.ValueU32 End Get Set(ByVal value As UInteger) Me.m_ebx.ValueU32 = value End Set End Property Private Property BX() As UShort Get Return Me.m_ebx.ValueU16 End Get Set(ByVal value As UShort) Me.m_ebx.ValueU16 = value End Set End Property Private Property BH() As Byte Get Return Me.m_ebx.ValueHighByte End Get Set(ByVal value As Byte) Me.m_ebx.ValueHighByte = value End Set End Property Private Property BL() As Byte Get Return Me.m_ebx.ValueLowByte End Get Set(ByVal value As Byte) Me.m_ebx.ValueLowByte = value End Set End Property #End Region #Region "ECXレジスタ" Private m_ecx As Register Private Property ECX() As UInteger Get Return Me.m_ecx.ValueU32 End Get Set(ByVal value As UInteger) Me.m_ecx.ValueU32 = value End Set End Property Private Property CX() As UShort Get Return Me.m_ecx.ValueU16 End Get Set(ByVal value As UShort) Me.m_ecx.ValueU16 = value End Set End Property Private Property CH() As Byte Get Return Me.m_ecx.ValueHighByte End Get Set(ByVal value As Byte) Me.m_ecx.ValueHighByte = value End Set End Property Private Property CL() As Byte Get Return Me.m_ecx.ValueLowByte End Get Set(ByVal value As Byte) Me.m_ecx.ValueLowByte = value End Set End Property #End Region #Region "EDXレジスタ" Private m_edx As Register Private Property EDX() As UInteger Get Return Me.m_edx.ValueU32 End Get Set(ByVal value As UInteger) Me.m_edx.ValueU32 = value End Set End Property Private Property DX() As UShort Get Return Me.m_edx.ValueU16 End Get Set(ByVal value As UShort) Me.m_edx.ValueU16 = value End Set End Property Private Property DH() As Byte Get Return Me.m_edx.ValueHighByte End Get Set(ByVal value As Byte) Me.m_edx.ValueHighByte = value End Set End Property Private Property DL() As Byte Get Return Me.m_edx.ValueLowByte End Get Set(ByVal value As Byte) Me.m_edx.ValueLowByte = value End Set End Property #End Region End Class
この作業を通じてRegister
構造体メリットが実感できると思います。Register
構造体がなければ、すべてのプロパティにビットごとの論理積を求めなくてはなりません。こんな状態では誰でもミスをおかします。プログラミングはミスがつきものですから、なるべくミスがおきにくい状態に持っていくことが肝要です。
なお、全プロパティの型は符号なしに設定します。理由は、数値の符号の有無は機械語で決まるからです。もし、符号ありの型にしてしまうと、符号を考慮しない機械語の命令の実装が難しくなってしまいます。