スポンサーリンク

ここからは実際に機械語を使ったプログラミングについて見ていきたいと思います。機械語のような単純な機能しか持たない、しかもたった17種類の命令だけで行うプログラミングはとても不便ではあるのですがその分、覚えなければならないことは少なくて済みます。

機械語の命令は2進数の数値で表されます。命令の数が少ないとはいえ全ての命令を数値で覚えるのはとても覚えにくいので、プログラムを考えるときにはまず各命令の名前(JUMP、LD、ADDなど)を使ってプログラムを書きます。アルファベットの名前で書かれたプログラムはアセンブラというソフトウェアを使って自動的に数値に変換し、メモリ回路に書き込んでからコンピュータをスタートさせます。アセンブラに入力する前の、命令の名前などを使って書かれた状態のプログラムのことをアセンブリ言語と言います。

機械語の命令はCPUの設計によって異なります。どのCPUでも似たような命令はあるのですが基本的にはCPUの種類の数だけ異なる機械語があると考えることができます。今回作成したオリジナルのCPUには当然ですが他社のCPU用アセンブラは使えません。アセンブラがない時に、アセンブラが行っている変換作業を手作業で行うことを「ハンドアセンブル」と言います。とても原始的なプログラミングの方法です。ここではハンドアセンブルで自作CPUのプログラミングを行う方法を紹介していきたいと思います。

アセンブリ言語で何らかの計算を行う時には必ず計算用のレジスタに数値を読み込んでから行います。今回作成したCPUでは「WR」レジスタが計算用レジスタになります。このようなレジスタは何らかの計算や作業を行うためのレジスタという意味でワーキングレジスタと呼ばれます。

LD命令(ロード命令)とST命令(ストア命令)はメモリ回路とワーキングレジスタの間で数値を移動させるときに使われます。アセンブリ言語では計算対象の数値をメモリからワーキングレジスタへ読み込み、計算が終わったらまたメモリに保存するという作業が頻繁に行われます。例えばメモリの8番地に書き込まれている数値と9番地に書き込まれている数値を足し算し、答えを10番地に書き込む、という作業をコンピュータにさせたい場合のプログラムは以下のようになります。ここでは例として8番地に12を、9番地に3を書き込んでおくことにします。答えは15になるはずです。

(以降ではアドレス番号を【 】(隅付き括弧)で囲んで表し、】の右側にそのアドレスの内容を記述します。)


【 0 】 LD	【 1 】 8
【 2 】 ADD	【 3 】 9
【 4 】 ST	【 5 】 10
【 6 】 JUMP	【 7 】 0
【 8 】 12	【 9 】 3
【 10】 0

 

このCPUの命令は1つの命令が16ビットのサイズを持っているので、2つ分のアドレス領域が常に1セットになって1つの命令を表現することになります。1つの命令はプログラムの1行で書けた方が分かりやすいと思うので、上のようにアドレスを2列にしてプログラムを書くことにします。

まずは0番値のLD命令によって計算対象の数値をワーキングレジスタへ読み込みます。0番地のLD命令の2つ目のアドレス領域(1番地)には8が書き込まれているので、このLD命令によって8番地の内容がワーキングレジスタへ読み込まれます。8番地の内容は12ですので、この段階でワーキングレジスタに書き込まれている内容は12になります。

次に2番地のADD命令によってワーキングレジスタの内容と、命令の2つ目のアドレスに書かれた9番地の内容が足し算されてワーキングレジスタへ上書きされます。この段階で最初にワーキングレジスタに入っていた12という数値と9番地の内容である3が足し算されて、ワーキングレジスタの内容は15に上書きされます。

4番地のST命令によってワーキングレジスタの内容がメモリに保存されます。命令の2つ目のアドレスには10が書き込まれているので、ワーキングレジスタに入っている15という数値がメモリの10番地へ書き込まれます。

以上でこのプログラムの目的である8番地の内容と9番地の内容を足し算して10番地に保存するという作業は完了しました、しかしこのままではコンピュータは以降の番地の内容を次々と命令として読み込み、実行しようとしてしまいます。例えばこのプログラムの8番地に書き込まれた12という数値は足し算の計算対象として書き込んである数値なのですが、このままプログラムの処理が進んでしまうとプログラムカウンタの値が8番地になった段階で12という数値が命令としてコンピュータに処理されてしまうことになります。この場合、12という数値がインストラクションレジスタの「IR1」へ読み込まれて処理されます。12という数値は2進数で1100なのでSL命令が実行されてしまいます。

このような状況を起こさないために、プログラミングでは処理の流れが意図しない場所へ行かないように管理する必要があります。今回のプログラムでは6番地でJUMP命令を置くことによって、処理の流れが8番地以降に行かないようにしています。6番地のJUMP命令の2つ目のアドレスには0が書き込まれているので、この段階で処理の流れは0番地へと飛ばされます。それ以降は再び0番地の命令から順にこれまでと同じ処理が実行され、6番地で処理が最初に戻るという動作が繰り返し実行されます。

このような繰り返しの動作はループ処理とも呼ばれています。

スポンサーリンク

アセンブリ言語でのコードの意味を確認したところで、次はコードを数値に変換する「ハンドアセンブル」の作業を行っていきたいと思います。以下に示す各命令と機械語の対応表を参照しながらアセンブリ言語のコードを機械語の数値に変換していきます。対応表とは言いますが昇順で数値が並んでいるだけですので簡単です。

▪️各命令の機械語への対応表


JUMP	:1
JZC	:2
LD	:3
ST	:4
SET	:5
LHI	:6
ADD	:7
SUB	:8
AND	:9
OR	:10
NOT	:11
SL	:12
SR	:13
INA	:14
INB	:15
OUTA	:16
OUTB	:17

 

機械語への対応表に従って上記のアセンブリ言語のコードを機械語へ変換すると以下のようになります。


【 0 】 3		【 1 】 8
【 2 】 7		【 3 】 9
【 4 】 4		【 5 】 10
【 6 】 1		【 7 】 0
【 8 】 12	【 9 】 3
【 10】 0

 

この数値をメモリに書き込んでコンピュータを起動させるとプログラムが動きます。別ページの「FPGAVerilog HDLで作るCPU」の方で実際にFPGA上にCPUを作った方は以下のような形で”cpu.mif”に機械語を書き込むことでプログラムを実行することができます。(”cpu.mif”というファイルはメモリ回路の内容を初期化するためのファイルです。表示されている番地に数値を書き込むことでメモリ回路の各番地の初期値を指定することができます。”cpu.mif”の変更内容を適用するにはQuartus上で再度コンパイルを行う必要があります。)

図77.”cpu.mif”に書き込まれた機械語

 

ここまでの内容で何となくアセンブリ言語でのプログラミングの様子が伝わったのではないかと思います。アセンブリ言語でのプログラミングではとても単純な命令の組み合わせでプログラムを作っていくので、大規模なプログラムを作ろうとするととても時間がかかってしまいます。この問題を解決するため、より効率的にプログラムを作れるように開発されたのが現在あるさまざまなプログラミング言語たちです。一般的なプログラミング言語はプロのエンジニアが効率よくプログラムを作れるように進化してきました。なので新しい言語になればなるほど複雑さが増し、習得するのにも時間がかかってしまいます。アセンブリ言語はそういう意味ではプログラミングとは何か?ということを最初に理解するには適した言語ではないかと思います。

 

<戻る       次へ>

 

スポンサーリンク