ビリーへの質問:CPUレジスタについて
2014.1
やぁみんな、ビリーだよ。
今年もよろしくね!
さて、今日はビリー宛てにもらった質問を紹介しよう。
読者からの質問
PC(Program Counter)レジスタとLR(リンクレジスタ)について教えていただけませんか?
ビリーからの回答
質問ありがとう。
スタックってなあに?(1)で、SPレジスタやFPレジスタの説明をしたんだけど、これを読んでくれての質問みたいだね。PC(Program Counter)レジスタとLR(リンクレジスタ)、それぞれについて、説明するね。
PC(Program Counter)レジスタ
PC(Program Counter)レジスタは、現在CPUがプログラムのどこを実行中であるかを格納しておくレジスタだ。基本的にはどんなCPUにでも存在するよ。
CPUは、メモリ上(RAMやフラッシュメモリ等)に置かれたプログラムの機械語コード(アセンブラ命令)を読み出して実行するのだけど、PCレジスタには実行中の機械語コードが置かれているメモリアドレスが入っているんだ。
このレジスタは、通常はプログラムから書き換えることはせず、アセンブラの分岐命令(C言語でサブルーチン呼び出しを行った場合など)が実行されると、分岐先のアドレスに書き換えられるよ。
例えば以下のような C言語のコードで、
testA 関数のコードはメモリ中の 0x5000番地
testB 関数のコードはメモリ中の 0x5700番地
にあったとする。
余談だけど、これらの関数のアドレス(番地)はプログラムのコンパイル・リンクを行う際のリンク実行時に決まるよ。
testA 関数のアセンブラコードは、例えば以下のようになる。
JSRはtestB(=0x5700)番地に分岐する、というアセンブラ命令。JSRという名称はCPUによって異なるからね。
testAはメモリ中の0x5000番地にあるけど、(省略1)のコードがあるから、JSR命令部分のコードは0x5048 番地ぐらいにあると仮定しよう。
JSR命令を実行する時点で、PCレジスタには0x5048が入っているよ。ここでJSR命令が実行されると、PCの現在値(0x5048)をどこかに覚えておいて(詳しくは後述)、PCには0x5700が代入され0x5700番地のtestB関数が実行される、という動作となるんだ。
LR(LinkRegister)レジスタ
LR(リンクレジスタ)は、分岐命令(サブルーチン呼び出し)が行われたときに、サブルーチン終了後に呼び出し元に戻ってこられるよう、現在の PC 値(正確には、現在の PC 値の次の命令のアドレス)を覚えておくためのものだよ。
このレジスタはPCレジスタとは異なり、すべてのCPUにあるわけじゃないんだ。例えばARMやPowerPC系のCPUには存在するけど、Intel x86系やSH系のCPUには存在しないよ(※2月7日修正:SHシリーズには同じ機能のPR(プロシジャレジスタ)をもつものがあります)。
PCレジスタの説明で、「JSR命令を実行するときに、PCの現在値をどこかに覚えておいて、」というのが出てきだけど、この「どこか」がリンクレジスタになるわけなんだ。
リンクレジスタが無いx86などのCPUでは、呼び出し元のPC値をスタックに積んでおき、呼び出し元に戻るときにスタックか PC値を復元しているよ。
ではリンクレジスタが存在するCPUで、サブルーチン呼び出しが二重三重になった場合はどうするのか、リンクレジスタは1個しかないじゃん、という疑問が出て来るけど、こんな場合はやっぱりスタックが使われるんだ。
今回は質問に対する回答ということで、PCレジスタとリンクレジスタの説明をしたけど、CPUレジスタについては、一度ちゃんと整理して説明したほうがいいかもしれないね。アセンブラでプログラムを書くことはあまりないかもしれないけれど、C言語と併用する場合なんかは、レジスタをちゃんと理解しておかないといけない。レジスタはCPUによって違うから、一般的な説明は難しいんだけどね。