矢沢久雄 グレープシティ アドバイザリースタッフ
今回は,アセンブラの命令の種類と,アセンブラでプログラムの流れを表す方法を説明します。皆さんは,コンピュータには様々な機能があると思っていることでしょう。ところが,アセンブラの視点すなわちハードウエアの視点から見れば,コンピュータにはわずかなことしかできないことが分かります。「データの入出力」「データの演算」「プログラムの流れの制御」の3つだけです。これら3つの動作を組み合せて,高度で複雑なプログラムの機能が実現されているのです。本当かなと思われるかもしれませんが,この記事を読めば,納得できるはずです。
●アセンブラの命令の種類=CPUにできること
CASL IIの命令(ニーモニック)を紹介しましょう。CASL IIの命令の種類は,COMET IIというハードウエアが持つCPUにできることを網羅したものです。命令の種類を丸暗記する必要などありません。上記の3つの動作に合わせて分類した表1~表3をざっと見ておいてください。
表1●データの入出力を行う命令
ニーモニック | 意味 | 機能 |
LD | LoaD | メモリーからCPUへデータを読み込む |
ST | STore | CPUからメモリーにデータを書き出す |
LAD | Load ADdress | CPUのレジスタに値を設定する |
PUSH | PUSH | スタックにデータを書き出す |
POP | POP | スタックからデータを読み込む |
SVC | SuperVisor Call | I/Oとの入出力を行う |
表2●データの演算を行う命令
ニーモニック | 意味 | 機能 |
ADDA | ADD Arithmetic | 算術加算を行う |
ADDL | ADD Logical | 論理加算を行う |
SUBA | SUBtract Arithmetic | 算術減算を行う |
SUBL | SUBtract Logical | 論理減算を行う |
AND | AND | 論理積(AND演算)を行う |
OR | OR | 論理和(OR演算)を行う |
XOR | eXclusive OR | 排他的論理和(XOR演算)を行う |
CPA | ComPare Arithmetic | 算術比較を行う |
CPL | ComPare Logical | 論理比較を行う |
SLA | Shift Left Arithmetic | 算術左シフトを行う |
SRA | Shift Right Arithmetic | 算術右シフトを行う |
SLL | Shift Left Logical | 論理左シフトを行う |
SRL | Shift Right Logical | 論理右シフトを行う |
NOP | NO Operation | 何もしない |
表3●プログラムの流れを制御する命令
ニーモニック | 意味 | 機能 |
JPL | Jump on PLus | 演算結果がプラスの場合に分岐する |
JMI | Jump on MInus | 演算結果がマイナスの場合に分岐する |
JNZ | Jump on Non Zero | 演算結果がゼロでない場合に分岐する |
JZE | Jump on ZEro | 演算結果がゼロの場合に分岐する |
JOV | Jump on OVerflow | 演算結果が桁あふれの場合に分岐する |
JUMP | JUMP | 無条件で分岐する |
CALL | CALL | 主プログラムから副プログラムを呼び出す |
RET | RETurn | 副プログラムから主プログラムに戻る |
スーパーバイザ・コールは,SVCというニーモニックで示していますが,実際には,IN(I/Oからデータを入力する)やOUT(I/Oへデータを出力する)などのニーモニックが使われます(これはCASL IIの都合ですので気にしないでください)。演算命令の機能には,「算術~」というものと「論理~」というものがあります。2進数の最上位ビット(符号ビット)が1となっているデータを,「算術~」ならマイナスの値とみなし,「論理~」ならプラスの値とみなします。
シフトとは,2進数の桁をずらす演算のことです。何もしないNOP命令は,プログラムで時間待ちをする場合などに使われます。何もしないのですが,演算命令に分類しておきました。
各種の分岐命令は,直前の演算命令の結果に応じてプログラムの流れを分岐させます。CALLとRET命令は,副プログラムの呼び出しで使われます。今後の連載の中で具体的なサンプル・プログラムを示しますので,今は細かいことを気にしないで,全体の雰囲気をつかんでください。たった20数個の命令だけで,様々なプログラムを作れることに驚いてください。
●アセンブラの文法は1つだけ
図1●アセンブラの文法は「命令+目的語」だけ |
![]() |
「命令+目的語」すなわち「○○せよ+△△に□□を」を書き並べただけのものが,アセンブラのプログラムです。ただし,必要に応じて「ラベル」や「コメント」を付け加えることもできます。
ラベルとは,アセンブラのプログラムのオペランドに名前(ラベル名)を付けたものです。この名前は,行のメモリー・アドレスを表しています。プログラムの中で123番地のような具体的なメモリー・アドレスの値を指定するのは面倒なので,ラベルを使うわけです。ラベルは,行の先頭に書きます。プログラムをマシン語に変換すると,ラベルの名前がその位置のメモリー・アドレスの値に置き換わります。
コメントは,プログラマが任意に記入する注釈です。行の末尾にセミコロン(;)に続けて記入します。コメントを漢字で書くこともできます。コメントは,マシン語に変換されません。
かつて,アセンブラ用の「コーディング用紙」といういうものが市販されていました。コーディング用紙とは,手書きでプログラムを記述するためのものです。ラベルとコメントを含めると,アセンブラのプログラムの1行の構成要素は,左から順に「ラベル,命令,目的語,コメント」の順になります。コーディング用紙では,命令のことを「オペコード」と呼び,目的語のことを「オペランド」と呼びます。表4は,100と200を加算した結果をANSというラベルが表すメモリー・アドレスに格納するプログラムをコーディング用紙に記述したものです。コメントを参考にして,プログラムの流れを追ってみてください。
表4●加算を行うプログラム
ラベル | オペコード | オペランド | コメント |
LD | GR1,A | ;GR1にA番地のデータを読み出す | |
ADDA | GR1,B | ;GR1とB番地のデータを加算する | |
ST | GR1,ANS | ;加算結果をANS番地に格納する | |
RET | ;主プログラムに戻る | ||
A | DC | 100 | ;A番地に100というデータを格納しておく |
B | DC | 200 | ;B番地に200というデータを格納しておく |
ANS | DS | 1 | ;ANS番地にデータの格納場所を確保しておく |
表4に示したプログラムでは,オペコード欄にDCやDSという命令が使われています。これらは,CPUに動作を行わせるのではなく,メモリー上にデータ領域を確保する命令であり「擬似命令」と呼ばれます。DC(Define Constant)は,データ領域を確保して値を格納します。A DC 100なら,100という値が格納されたメモリー領域を確保し,それをA番地とします。DS(Define Storage)は,データ領域の確保だけを行い値を格納しません。ANS DS 1なら,メモリー領域1個分(16ビット)を確保し,それをANS番地とします。AやANSが何番地になるかを気にする必要はありません。
ADDA GR1,Bの部分に注目してください。これは,GR1(値は100)とB番地のデータ(値は200)を加算するものです。加算結果である300は,どこに記録されるのでしょうか?実は,GR1に記録されるのです。GR1とB番地の加算結果が,再びGR1に記録されます。これは,アセンブラならではの特徴であり,ans = a + b のように,3つの変数を使って加算を行う高水準言語との大きな違いです。
●PCレジスタ,SPレジスタ,FRレジスタの役割
COMET IIのCPUには,PC,SP,FR,GR0~GR7というレジスタがあります。これらの中で,アセンブラのプログラムの目的語(オペランド)欄に指定できるのは,SPとGR0~GR7だけです。PCとFRレジスタの値は,プログラムの実行中にCPUによって自動的に設定されます。
PCレジスタ(Program Counter)は,プログラムの流れを制御するためのものです。アセンブラのプログラムの1行1行は,何番地かのメモリー・アドレスに格納されます。PCレジスタの値は,プログラムの起動時に1行目のメモリー・アドレスとなります。表4のプログラムなら,LD GR1,Aという行のメモリー・アドレスです。この行の命令の実行を終えると,CPUは,次の行(表4ならADDA GR1,Bの行)のメモリー・アドレスを自動的にPCレジスタに設定します。これによって,プログラムが1行目から順番に実行されて行きます。表3に示したプログラムの流れを変える命令を実行することでも,PCレジスタの値が自動的に設定されます。「自動的」とは,アセンブラのプログラムで直接PCレジスタに値を設定しないという意味です。
図2●FRレジスタの構造 |
![]() |
CPUは,2つのデータの大小を比較するときに,内部的に減算を行います。例えば,AとBを比較するなら,A - Bという減算を行います。AとBが等しければ,A - Bの結果がゼロになるので,FRレジスタのZero Flagが1になります。A>Bなら,A - Bの結果がプラスになるので,Sign Flagが0になります。A<Bなら,A - Bの結果がマイナスになるので,Sign Flagが1になります。比較結果は,Sign FlagとZero Flagでわかります(表5)。Overflow Flagは,演算結果が16ビットに収まらない大きな値になったとき,および右シフトで桁落ちが発生した場合に1になります。
表5●比較結果とFRレジスタの値
比較結果 | Sign Flag | Zero Flag |
A=B | 0 | 1 |
A>B | 0 | 0 |
A<B | 1 | 0 |
SPレジスタは,「スタック」と呼ばれるデータ格納領域をメモリー上に作るために使われます。スタックには,PUSH命令でデータを格納でき,POP命令でデータを取り出せます。データが格納されるメモリー・アドレスやラベルを指定しないでよいのが,スタックの便利なところです。
スタックを作るためには,プログラムの最初で,スタックとするメモリー領域のアドレスをSPレジスタに設定しておきます。PUSH命令は,SPレジスタが指すメモリー・アドレスにデータを格納し,SPレジスタの値を自動的に-1します。POP命令は,SPレジスタが指すメモリー・アドレスからデータを取り出し,SPレジスタの値を自動的に+1します。
図3●SPレジスタが現在の読み書き位置を示す |
![]() |
●プログラムの流れ
プログラムの流れには「順次進行」「条件分岐」「繰り返し」の3種類があります。これは,アセンブラに限らず,あらゆるプログラミング言語で同じです。
順次進行とは,命令が記述された順に実行されていくことです。プログラムは,基本的に順次進行で流れて行きます。先に示したプログラムの流れも順次進行であり,上の行から下の行に向かってプログラムが流れます。
リスト1●条件分岐を行うプログラム |
![]() |
リスト2●繰り返しを行うプログラム |
![]() |
繰り返しとは,プログラムの同じ部分を何度か繰り返して実行することです。アセンブラでは,何らかのレジスタで回数をカウントし,CPAまたはCPLで繰り返し回数に達していないことを確認して,JPLやJMIなどの命令でプログラムを前の行に戻すことで繰り返しが実現されます。リスト2[拡大表示]は,GR1レジスタで回数をカウントしながら10回の繰り返しを行うプログラムです。ここでは,GR1レジスタの初期値が0に設定されていて,N番地に10というデータが格納されているとします。
アセンブラの命令の種類が「データの入出力」「データの演算」「プログラムの流れの制御」の3つだけであり,アセンブラの文法が「命令+目的語」だけであることを知りました。コンピュータとは,実にシンプルなものだと感じたでしょう。シンプルなコンピュータを使って,いかに高度で複雑なプログラムを作成するかは,プログラマの腕次第なのです。
次回は,プログラムを主プログラムと副プログラムに分ける方法を説明します。どうぞお楽しみに!
■変更履歴 リスト1の2行目でLL1、リスト2の一番下でLBL3としていましたが、どちらも正しくはLBL1です。お詫びして訂正します。本文は修正済みです。 [2010/05/11] |