アセンブリ言語の基礎知識 Part 5

●分岐命令とレジスタ

 さて次は、分岐命令についてお話しします。CPUの命令は大きく分けて転送、演算、分岐に分けられます。まあ細かくほかにもありますが、大きく分けるとこの3つです。
 転送、演算については、述べましたので、次に分岐命令の解説をします。

 分岐とは、プログラムの流れを変えることです。通常、分岐が無ければ、処理はアドレスの順番に実行されていきます。分岐されれば、その流れが変わり、その分岐で指定されたアドレスへ処理が移ります。
 さて、CPUは電源を投入後、メモリの先頭番地から実行していき、もし分岐がなければ、延々メモリの最終番地まで実行され、また先頭番地へ実行が戻ります。この「先頭に戻る」という動きは結構重要です。どういった理由で戻るのか。次のレジスタの表を見てください。
 

レジスタ
SP
PC

 分岐において重要なものにPCレジスタがあります。このレジスタはプログラムカウンタというレジスタです。CPUはメモリのコードやデータを読みながら実行されていくわけですが、当然、今実行しているアドレスを記憶しておかなければ、次にどこを実行するのかわかりません。そこで用意されているのが、このPCレジスタです。このレジスタもSP同様、機能レジスタになります。  

CPUの制御手順
手順 動作内容
PCレジスタ→アドレスレジスタ 現在のアドレスを記憶
PC+1→PC PCレジスタをカウント
(アドレスレジスタ)→命令解析レジスタ 命令をメモリから読み込む
命令実行 命令の処理
 

 上の表のアドレスレジスタとは、CPUが内部処理に使用するものであり、アドレスの一時保存を行うためのものです。一般的にCPUは表のような手順を繰り返していきます。
 そしてPCレジスタは実行していくごとに格納されているアドレス値を自動的に+1、加算していきます。このPCの値で、CPUは次にどのアドレスの命令を読んで実行するのかが決まるわけです。PCはこの場合16ビットであり、もしこの値が、FFFFhまでいき、次に+1加算すると、このカウンタは0000hへ戻ってしまいます。これが、メモリの先頭アドレスに処理が戻る理由なのです。

 このことからCPUはPCの値通りに処理していくということが言えるわけですから、PC値を変えることはすなわち、次に実行されるアドレス指定できることになります。これが、分岐したことになり、分岐命令ではこのPCを変化させることを言います。  

分岐命令仕様
ニーモニック オペレーション
JP nn PC ← nn
 

 上の表の命令はジャンプ命令といい、nnで指定したアドレスをPCレジスタへ入れる処理をします。その結果、指定したアドレスへ処理が移り、プログラムの流れが変わる命令であるのです。 

 では実際に簡単な例を見てみましょう。
 

足し算プログラムのループ
番地 コード アセンブリ言語
0000h
0001h
3Eh
0Ah
LD A,10
0002h
0003h
06h
16h
LD B,20
0004h 80h ADD A,B
0005h
0006h
0007h
C3h
00h
00h
JP 0000h

 この表のプログラムは以前にやった足し算プログラムをループさせるものです。0005h〜0007hにジャンプ命令があり、0000hへ処理を移すようになっています。結局0000h〜0007hまでを永久に繰り返すプログラムです。
 このプログラム自体はただ同じことを繰り返すだけで結果的に繰り返しに意味はないんですけど・・・ちゃんとしたのを作れって言われそう(笑)

●スタックと分岐命令

 ここまでのジャンプ命令を利用して次のような流れを作ります。  

ジャンプ命令での分岐例
番地 プログラム
0000h
処理A
0010h JP 0023h
0013h
処理B
0020h JP 0000h
0023h
処理C
0030h JP 0013h

 この場合、処理される順番はA、C、Bの順で処理されまたAに戻っています。  このジャンプ命令はアドレスを直接指定してその場所へ移ったわけですが、分岐命令は、もう一つ違った種類のものがあります。それはCALL命令といいサブルーチンコールというものを行う命令です。 

分岐命令仕様
ニーモニック オペレーション
CALL nn (SP) ← PC
PC ← nn
RET PC ← (SP)
 

 CALL(コール)命令のオペレーションを見るとまず(SP)(SPが示すメモリアドレス)のメモリへPCのアドレスを保存してからPCを書き換えジャンプしています。そして、RET(リターン)命令は保存してある(SP)にあるのアドレスをPCへ戻しています。
 これはどういうことが、次のプログラムを見てください。  

CALL命令を使った分岐例
番地 プログラム オペレーション
0000h
処理A  
0010h CALL 0023h (SP)←PC(0013h)
PC←0023h
0013h
処理B  
0020h JP 0000h 処理A
0023h
処理C  
0030h RET PC←(SP) (0013h)

 処理Aの後にCALL命令があります。ここではまず現在のPCのアドレスをスタックへ積んでいます。このときのPCの値は命令実行の前にカウントされていましたから、CALL命令の次のアドレス0013hになります。
 スタックを積んだ後、ジャンプ同様0023hをPCへ入れ、処理Cへ移ります。
 そして、処理Cの終わりにはRET命令があります。この命令のオペレーションではスタックに積まれた値をPCに戻す動きをします。
 結局、CALL命令でスタックにCALL命令の次のアドレスが積まれていましたから、このRET命令だけで、処理Bへ移る(戻る)ことができますよね。

 このようにCALL命令とRET命令はセットで使用されます。ジャンプする前にCALL命令の次のアドレスをスタックへ積み、今度はRET命令で、その値を戻す。これによりRET命令の場所では、戻るアドレスがスタックに積んであるので、直接戻る場所の指定をしなくてもよくなります。
 この特徴を利用して、  

コール命令のサブルーチン
番地 プログラム オペレーション
0000h
処理A  
0010h CALL 0033h (SP)←PC(0013h)
PC←0033h
0013h
処理B  
0020h CALL 0033h (SP)←PC(0023h)
PC←0033h
0023h
処理C  
0030h JP 0000h 処理A
0033h
処理X  
0040h RET PC←(SP)

 のような処理ができます。
 この場合、0010hと0020hで同じ0033hをコールしています。どちらもスタックへ処理途中のアドレスを積んでから0033hである処理Xを呼び出しています。そして、0040hのRET命令では積んであるスタックのアドレスをPCへ入れるため、どちらのコールも呼ばれた次のアドレスに処理を戻すことができます。
 このように、CALL命令で呼び出される処理Xの部分「サブルーチン」とよび、それ以外をメインルーチンと言います。
 CALL命令はどこでも、何回でも、サブルーチンを呼び出し、処理終了後はRET命令で呼び出された元の流れに帰ることができるのです。
 このことからサブルーチンは同じ処理を何回も行う必要がある場合に、このような形でサブルーチン化させることができるわけです。

 このCALL、RET命令は大変便利で、同じ処理を繰り返すこともさることながら、呼び出すアドレスと、その処理内容さえ分かればどこからでも呼び出せ、戻る場所のアドレスは考える必要がないのです。
 このようなことで、CALL、RET命令はジャンプ命令とはまた違った特徴を持つ分岐命令なのです。

●おまけ

 「機械には押してはいけないボタンがある」っていうのはロボットものとかの定番(?)ですよね。実は以前、仕様書を見ていたときにZ80CPUにはHALTって命令が載ってました。あたしゃこわくて押した(!?)ことはないんですけど、この命令CPU動作を停止する命令らしいです。あっ間違っても、CPUが壊れるとか、自爆するとか(爆)いうわけではないですよ。それだとかなり怖い命令だ(笑)。
 仕様書ではCPUの実行を停止させる命令となっています。まあたぶんCPUがただ止まるんでしょうけど、何故あるのかは不明な命令です。作った人が機械には付き物だ!って思っていたのかもしれませんがね(おぃおぃ)。そういえば・・・最新のCPUにもこのようなコードはあるんだろうか・・・・。