はじめに
本記事はVB.NETの初歩的な記法だけを使って、簡単な機械語で動く仮想CPUの実装法を解説します(※CPUにもいろいろありますが、この記事ではIntel社が製造しているCPUを対象とします)。その過程を通じて、初心者でもバイナリプログラミングが楽しめることと、バイナリプログラミングの魅力を伝えたいと思います。
今回はINC命令とDEC命令の実装を通じて、長さが違う機械語の実装法を解説します。
これまでの連載
- VB.NETで仮想CPUを作ろう
- VB.NETで仮想CPUを作ろう (2) - レジスタの実装
- VB.NETで仮想CPUを作ろう (3) - 仮想CPUのGUI化
- VB.NETで仮想CPUを作ろう (4) - テストドライバの改良
- VB.NETで仮想CPUを作ろう (5) - CPUの基礎動作の実装
- VB.NETで仮想CPUを作ろう (6) - MOV命令実装
- VB.NETで仮想CPUを作ろう (7) - ADD命令実装
- VB.NETで仮想CPUを作ろう (8) - SUB命令実装
下準備
前回の実装を拡張していきますので、あらかじめ前回までの部分の実装を済ませておいてください。後は専門用語とCPU構造の確認のため、前回用意した3つのIntel社のマニュアルをすぐ読める状態にしてください。
それに加えて、中巻 B: 命令セット・リファレンス N-ZのA-6とA-7ページを印刷しておくことをお勧めします。この表をあらかじめ印刷しておき、その紙を参照しながら実装作業をすると効率がよくなります。
機械語の命令長について
今回まで紹介してきた機械語の命令はどれも2バイト(命令1バイト+即値1バイト)もしくは5バイト(命令1バイト+即値4バイト)でした。しかし、実のところすべての命令がそうではありません。命令を表すのに2バイト必要な命令もあれば、メモリを使用する場合に、即値の他にメモリアドレスなどの値を指定したりすることもあります。
機械語ではこのように必要とするバイト数がその都度変化します。
すべての機械語が個定数だと便利なのですが、CPUの製造メーカーが完璧な命令を実装できるわけではありませんし、過去のプログラムを動かすための互換性維持などのさまざまな理由で長さを一定に保てません。
それに、命令の長さが変化することに利点もあります。それは、パフォーマンス向上効果です。よく使われる命令を少ないバイト数で表現できれば、すべての命令が個定数の時は違って、全体としての無駄なバイトを減らすことができ、その結果パフォーマンスの向上をもたらします。
なお、命令が必要とするバイト数を命令長と呼びます。
命令長への対応
命令長が異なる命令を扱うには、まず「命令長」の情報が必要となります。その命令長はどこに属するべきかと言うと、命令を表現しているOpeCode
構造体です。始めから複雑な処理を考えるとよい結果を生みませんので、プロパティとして実装することにします。OpeCode
メソッドにコメントを追加して掲載します。
Public Structure OpeCode '命令の名前 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 End Structure
今回はより分かりやすいように、すべてのプロパティに説明を加えました。実務ではコメントは重要で、簡単で説明がなくても分かるというコードでもひとことぐらいは書いておきます。そうしないと、実装した人には当たり前のことでも、何年も経った後に他者が修正を加える場合などに困ってしまうからです。
実務と趣味のコーディングの違いは、「他者を意識する」ということです。常に他者から見ても分かるかを意識してコーディングをしましょう。 実はOpeCode
構造体以外にも命令長を保持するべきクラスがあるのですが、今説明すると分かりにくいので、今までの命令に対して命令長の情報を付加する方法を考えます。