こんにちは、めのんです!
週に1回しか投稿していないと以前何を書いていたのかわすれちゃいますね。
見返してみて前回がスタックポインタだったことをやっと思い出しました。
今日はプログラムカウンタを考えることにします。
プログラムカウンタとは?
プログラムカウンタ(略してPC)は次に実行する場所が格納される一種のレジスタです。
具体的には、次の命令フェッチで読み込む命令のアドレスが入っています。
「命令フェッチ」はメモリから命令を読み込むことだと考えてください。
インテル系のCPUではインストラクションポインタ(IP)と呼ぶようですので、こちらの方が馴染みがある方もいらっしゃるかもしれませんね。
命令フェッチのたびにPCはインクリメントされますし、分岐が発生すれば分岐先のアドレスがPCにロードされます。
サブルーチンの呼び出しではサブルーチンから戻ったときにPCにロードするアドレスがスタックに待避されます(アーキテクチャによってはひとつ前のアドレスが待避されることもあります)。
ATmega329のプログラムカウンタ
先ほどは一般的なプログラムカウンタについてお話ししました。
今度はATmega328特有のことをお話ししたいと思います。
ATmega328では、汎用レジスタやスタックポインタなどのレジスタにアドレスが振られています。
ところがプログラムカウンタにはアドレスが振られていません。
ですので、プログラムカウンタの読み書きはそれほど簡単ではありません。
プログラムカウンタへの書き込みは分岐命令を使えばできますし、プログラムカウンタの値を読み込む際にはアセンブラを使えば現在位置の取得は簡単にできますから、実際には不便はないのだと思います。
ATmega328のプログラムメモリは以前にも紹介したように0x0000から0x3fffまでのアドレスが振られています。
プログラムカウンタはこのプログラムメモリ上のアドレスを表現できればいいので、16ビットのレジスタになっています。
プログラムメモリはひとつのアドレスに16ビットの値が格納されることを覚えていらっしゃいますでしょうか?
AVRの命令は16ビットまたは32ビットですので、命令が実行されるたびにプログラムカウンタが1または2ずつ進むことになりますね。
プログラムカウンタの初期化
プログラムカウンタの初期化はCPUのリセットによって行われます。
CPUに電源が入ると、リセット端子に一定時間LOWレベル(=0ボルト)の信号が入力されます。
これによってリセットがかかるようになっています。
電源が入ったときだけではなくて、同じようにリセット端子にLOWレベルを一定時間(=2.5μs)以上入力すればあとからでもリセットすることができます。
それ以外にも、今回は詳しいことは書きませんが、ソフトウェア的にリセットをかけることもできます。
リセットがかかるとプログラムカウンタは0x0000に初期化されます。
0x0000番地から0x0033番地までは割り込みベクタといって、割り込みが入ったときの分岐先になっています。
割り込みについては後日あらためてお話しするようにします。
なお、リセットも割り込みの一種です。
割り込みはいくつもの種類があります。
割り込みベクタには割り込みの種類ごとに2番地ずつ割り振られています。
つまり、リセットのためのベクタは0x0000と0x0001の2つの番地が割り振られています。
AVRの命令は16ビットまたは32ビットだと先ほどお話しましたね。
典型的な分岐命令であるJMPは32ビットの命令ですので、各割り込みベクタに
JMP 分岐先アドレス
のように書いておけば、任意のアドレスからプログラムを開始できるようになります。
命令セットシミュレータでの実装
AVRのプログラムカウンタにはアドレスがありませんので、単なる16ビット符号無し整数として扱えば十分です。
uint16_t pc; // プログラムカウンタ
あとは、リセットでpcに0x0000を代入し、順にプログラムメモリから命令を読み込んでいけばいいでしょう。
今回はこの程度にしておきます。
プログラムカウンタはそんなに難しい概念ではないのでご理解いただけましたでしょうか?
次回はステータスレジスタについて考えていきたいと思います。
どうぞご期待ください!