はじめに
本記事はVB.NETの初歩的な記法だけを使って、簡単な機械語で動く仮想CPUの実装法を解説します(※CPUにもいろいろありますが、この記事ではIntel社が製造しているCPUを対象とします)。その過程を通じて、初心者でもバイナリプログラミングが楽しめることと、バイナリプログラミングの魅力を伝えたいと思っています。
前回は、テストドライバをさらに改良して機械語の実装を行いやすい環境を整えました。今回から機械語の命令を実装する段階に入ります。
これまでの連載
- VB.NETで仮想CPUを作ろう
- VB.NETで仮想CPUを作ろう (2) - レジスタの実装
- VB.NETで仮想CPUを作ろう (3) - 仮想CPUのGUI化
- VB.NETで仮想CPUを作ろう (4) - テストドライバの改良
下準備
今回は前回の実装を拡張していきますので、あらかじめ前回までの部分の実装を済ませておいてください。後は専門用語とCPU構造の確認のため、前回用意した3つのIntel社のマニュアルをすぐ読める状態にしてください。
それに加えて、中巻 B: 命令セット・リファレンス N-ZのA-6とA-7ページを印刷しておくことをお勧めします。絶対に必要なことではありませんが、この表をあらかじめ印刷しておき、その紙を参照しながら実装作業をすると効率がよくなります。
命令読み込み機能の実装
機械語の命令をCPUが実行するためには、命令読み込み・命令解析・実行というステップを踏まなければなりません。ですからまずは、命令読み込み機能を実装する必要があります。
『VB.NETで仮想CPUを作ろう (3) - 仮想CPUのGUI化』でIIntelCpuクラスにReadBinary、AnalyzeBinary、ExecuteCommandの3つのメソッドを定義しておいたことを覚えているでしょうか? 命令読み込み機能はこのうちのReadBinaryメソッドに実装します。
まず最初に読み込んだバイナリデータを保存するためにフィールドを宣言します。
Private binarys As Queue(Of Byte)
読者の中にはQueueジェネリッククラスを使用する理由を知りたい方が居ると思いますので説明しておきます。配列は事前に数が分からないので扱いづらく、他のジェネリッククラス、例えばListでは無駄なデータを保持してしまうので、Queueがこの状況では適しています。というのも、解析した後のバイナリデータは二度と使用しないからです。無駄なデータをそのままにしておいてはエラーの元ですし、リソースの無駄です。要らないデータはすぐさま破棄しましょう。ですから、先入れ先出し方式で、出したデータを破棄するQueueを選びました。
次にこのbinarysフィールドを初期化するためにコンストラクタを定義します。
binarys = New Queue(Of Byte)
これで準備が整ったのでReadBinaryメソッドを実装します。
'バイナリ値を読み込む処理を行います。 Public Sub ReadBinary(ByVal BinaryDatas() As Byte) Dim data As Byte For Each data In BinaryDatas binarys.Enqueue(data) Next End Sub
拍子抜けした人も居ると思いますがこれで命令読み込み処理は実装完了です。
次は命令解析処理の実装を行います。
命令解析機能の実装
読み込んだバイナリデータから命令を分析する機能をAnalyzeBinaryメソッドに実装します。このメソッドもフィールドを用意します。
Private cmds As Queue(Of OpeCode)
次にコンストラクタにこのフィールドの初期化命令を追加します。
Sub New() binarys = New Queue(Of Byte) cmds = New Queue(Of OpeCode) End Sub
これで準備が整ったのでAnalyzeBinaryメソッドを実装します。
'読み込まれたバイナリ値を解析して実行する命令を特定します。 Public Sub AnalyzeBinary() Dim binary As Byte While binarys.Count <> 0 '指定されている命令とレジスタを決定 binary = binarys.Dequeue() Dim ope As OpeCode = SearchOpeCodeMap(binary) '値を導出 If ope.BitCount = 32 Then Dim values(3) As Byte For i As Integer = 0 To 3 values(i) = binarys.Dequeue() Next 'Intelはリトルエンディアンなので値を逆にする values = GetLittleEndianValue(values) '取得したバイト配列からダブルワードの値を構築 Dim value As UInteger = ConvertValues(values) ope.Value = New Register(value) Else ope.Value = New Register(binarys.Dequeue(), ope.IsHi) End If '実行する命令を記憶 cmds.Enqueue(ope) End While End Sub
CPUの基本動作の中で一番難しいところなので、今回の一番の難関です。しかし個々の命令は難しいことをしていませんので安心してこれ以降を読み進めてください。ポイントとなる処理を上から順番に解説していきます。
