ステータスレジスタ
ステータスレジスタは、フラグ ビットの集まりであり、マイクロプロセッサでの様々な演算の状態(ステータス)を示すものである。
これらのフラグは条件分岐に主に用いられる。
マイクロプロセッサは、そのアーキテクチャによって異なる状態フラグのセットを持っている。
目次
一般的なステータスレジスタの構成
ステータスレジスタは一般的な演算結果に関する状態を示すフラグを集めた部分と、CPUの動作を制御する部分がある。 前者は一般のユーザプログラムからもアクセス可能だが、後者は一般的にはオペレーティングシステムからしかアクセスできない。ただし、初期のマイクロプロセッサには特権レベル(あるいは特権モード)を持たない場合があり、そのようなプロセッサではステータスレジスタ全体にアクセスすることができた(別の言い方をすれば、ユーザアプリケーションとオペレーティングシステムの区別がなかった)。
ステータスレジスタの実例
以下にいくつかのマイクロプロセッサでのステータスレジスタ(フラグレジスタとも言う)の実装例を挙げる。 なお、ビット位置はLSBを0として統一している。 また、説明の中で「セット」は 1 を書き込むこと、「クリア」はゼロを書き込むことを示す。 説明がないフラグは既出の同じ名前のフラグと同じ働きをする。
特定のフラグの状態を調べるには、マスクを用いればよい。
6502
6502のステータスレジスタのサイズは8ビットであり、フラグバイトと呼ばれた。
- Bit 7. ネガティブフラグ:演算結果の正負を示す。ただし、6502の演算結果は常にAレジスタに格納されるため、結局はAレジスタのMSBを示している。
- Bit 6. オーバーフローフラグ:演算結果が符号付き8ビットで表せる範囲を超えたことを示す。
- Bit 5. 未使用。常に1。
- Bit 4. ブレークフラグ:BRK割り込み発生を示す。6502にはBRKとIRQの二種類の割り込みがあり、それを識別するためのフラグである。
- Bit 3. デシマルフラグ:セットするとBCDモードで動作する。
- Bit 2. インタラプトフラグ:セットすると割り込みが禁止される。
- Bit 1. ゼロフラグ:演算結果が 0 の時にセットされる。
- Bit 0. キャリーフラグ:キャリー発生時およびボローが発生しなかった時にセットされる。
Z80
Z80のステータスレジスタのサイズは8ビットであり、フラグレジスタと呼ばれた。
- Bit 7. S 符号フラグ。
- Bit 6. Z ゼロフラグ。
- Bit 5. 未使用。常に0。
- Bit 4. H ハーフキャリーフラグ(パックBCD演算用)。
- Bit 3. 未使用。常に0。
- Bit 2. P/V パリティ・オーバーフローフラグ(8080ではP パリティ)。
- Bit 1. N 減算フラグ(ADD命令で0、SUB命令で1になる。8080では未使用、常に0)。
- Bit 0. C キャリーフラグ。
8086
8086のステータスレジスタのサイズは16ビットであり、フラグレジスタと呼ばれた。
- Bit 15 - 12. 未使用
- Bit 11. オーバーフローフラグ
- Bit 10. ディレクションフラグ:ストリング操作命令で、ポインタの増減方向を示す。
- Bit 9. インタラプトフラグ:クリアすると割り込みが禁止される。
- Bit 8.トラップフラグ:シングルステップモードでの実行を制御する。
- Bit 7. サインフラグ:演算結果の正負を示す。
- Bit 6. ゼロフラグ
- Bit 5. 未使用
- Bit 4. 補助キャリーフラグ:BCD演算で使用されるキャリーフラグ
- Bit 3. 未使用
- Bit 2. パリティフラグ:演算結果の 1 となるビット数が偶数のときセットされ、奇数のときクリアされる。
- Bit 1. 未使用
- Bit 0. キャリーフラグ
Pentium
Pentiumのステータスレジスタのサイズは32ビットであり、EFLAGSレジスタと呼ばれた。
- Bit 31 - 22. 未使用
- Bit 21. IDフラグ:CPUID命令の使用を制御する。
- Bit 20. 仮想割り込みペンディングフラグ:仮想86モードでの割り込み関連のフラグ
- Bit 19. 仮想割り込みフラグ:仮想86モードでの割り込み関連のフラグ
- Bit 18. アラインメントチェックフラグ:メモリアクセスのアラインメントチェックを制御するフラグ
- Bit 17. 仮想86モードフラグ:仮想86モードの制御をするフラグ
- Bit 16. レジュームフラグ:デバッグ関連の制御フラグ
- Bit 15. 未使用
- Bit 14. ネストタスクフラグ:タスクがネストしているかどうかを示す。
- Bit 13 - 12. 割り込み特権レベルフラグ:動作中タスクの割り込み特権レベルを示す。
- Bit 11. オーバーフローフラグ
- Bit 10. ディレクションフラグ:
- Bit 9. インタラプトフラグ:
- Bit 8.トラップフラグ:
- Bit 7. サインフラグ:
- Bit 6. ゼロフラグ
- Bit 5. 未使用
- Bit 4. 補助キャリーフラグ:
- Bit 3. 未使用
- Bit 2. パリティフラグ:
- Bit 1. 未使用
- Bit 0. キャリーフラグ
68020
68020のステータスレジスタは16ビットで、上位8ビットをシステムバイト、下位8ビットをユーザバイトまたはコンディション・コード・レジスタ(CCR)と呼ぶ。
- Bit 15 - 14. トレースモードフラグ:デバッグ用制御フラグ
- Bit 13. スーパバイザフラグ:特権モードを示す
- Bit 12. マスタフラグ:マルチプロセッサ構成用のフラグ
- Bit 11. 未使用
- Bit 10 - 8. 割り込みマスク:3ビットで8レベルの割り込みマスクを表す。
- Bit 7 - 5. 未使用
- Bit 4. 拡張フラグ:キャリーフラグとほぼ同じ意味
- Bit 3. ネガティブフラグ
- Bit 2. ゼロフラグ
- Bit 1. オーバーフローフラグ
- Bit 0. キャリーフラグ
R3000
MIPSアーキテクチャでは、ステータスレジスタが存在しない。 これは、プログラムの流れを制御するにあたっては、分岐命令が条件付となっていて、レジスタ間の大小比較やゼロとの大小比較をした結果で分岐するかしないかを決めるために、ネガティブフラグやゼロフラグに相当するフラグを必要としないのである。 この設計方針には以下のような思想がある。
- 演算を行うたびにステータスレジスタを設定することはパイプラインの設計を複雑にする。
- 演算そのものに余分な処理を追加しなくてはならなくなる。
- 演算するたびにステータスをチェックするようなプログラムはありえない。つまり、必要なときだけ比較すればよい。
また、オーバフローやキャリーに相当する分岐条件はないが、これらは例外トラップを発生させることでオペレーティングシステムに処理を任せている。ただし、OSがこれらにたいしてうまく処理ができるわけではなく、ユーザプログラムが対応する例外処理をOS側に事前に登録しておかなければならない。そうでなければそれらの例外を発生させたプロセスを停止させることになる。また、OSが介在するため通常通る箇所で頻繁にオーバフローを起こすと性能低下を招くため、基本的にはそれらの例外が発生しないようにプログラムを組むことになる。
PowerPC
PowerPCのステータスレジスタは、4ビットのフラグ8組から構成されており、コンディションレジスタ(CR0 - CR7)と呼ばれる。
- Bit 0. 負数 - 演算結果が負
- Bit 1. 正数 - 演算結果が正 (0を含まず)
- Bit 2. ゼロ - 演算結果が0
- Bit 3. オーバーフロー XERレジスタのSOフィールドのコピー
演算命令実行時にCR0 - CR7のどのコンディションレジスタに結果を反映させるか(またはどこにも反映させないか)を指定できる。 また、条件分岐時にもどのコンディションレジスタから条件を読み取るかということを指定できる。 これにより、フラグ更新を伴う複数の演算を先に実施しておき、後で条件分岐を行うということが可能となる。
Itanium
Itaniumでは、従来のようなステータスレジスタは存在しない。というのもEPICアーキテクチャによって、複数の命令を同時に並行して実行するため、唯一のステータスレジスタでは結果を保持することができないためである。そこで、Itaniumでは64ビット幅で各ビットが汎用のフラグとして使えるプレディケートレジスタを用意している。この使用方法は次のようになる。
- 比較命令で何らかの比較を行う。レジスタ間あるいはイミディエート値とレジスタの大小比較ができる。
- 比較命令では比較結果を2つのプレディケートレジスタに格納する。一方がセットされれば、もう一方はクリアされる。
- 各命令は、必ずチェックすべきプレディケートレジスタを指定する。
- チェックすべきプレディケートレジスタが 0 の場合、その命令は実行しない。
- 条件分岐もプレディケートレジスタを指定し、その値によって分岐するかしないかを決定する。
このように、プレディケートレジスタを使うと、基本的にひとつのプロシージャ(サブルーチン)内では分岐命令を使わずに済む。 これにより、分岐によるパイプラインの乱れを減らすことになり、高速化に寄与する。C言語の if文の then 部と else部を同時に実行するようなものだが、たとえばelse部が非常に短い場合や存在しない場合、then部を通る場合はよいが、通らない場合には実行しない命令をずっと実行ユニットに送り続けることになるため、分岐したほうが有効な場合もある。
なお、ItaniumのIA-64モードでは前述のとおりだが、IA-32モードでは従来とおりPentium相当のステータスレジスタが存在する。
キャリー
テンプレート:Seealso キャリー(テンプレート:Lang-en-short)とは「繰り越す」という意味であり、その背景には当該CPUで直接処理できる以上のビット幅の数値を扱うための仕組みという考え方がある。これは筆算による計算と同じ手法である。4ビットのプロセッサを例として説明する。 4ビットで表せる符合なしの数値は 0 から 15 までである。 たとえば、 1001 (9) と 0111 (7) の加算は次のようになる。
1001 + 0111 = 10000
このように桁があふれてしまう。キャリーフラグを持つプロセッサでは、あふれた桁をキャリーフラグに格納する。 また、加算命令にはキャリーフラグの内容も同時に加算するという命令を持っている。 これを使うと、4ビット以上の演算が可能となる。たとえば、00101001 (41) と 00010111 (23) の加算は、次のような手順となる。
- 下位 4ビット(1001 (9) と 0111 (7))を加算する。(前掲の加算と同じ)
- 上位 4ビットにキャリーを加えて加算する。
1 <- キャリー 0010 + 0001 = 0100
この結果をつなげると 01000000 (64) となる。
減算の場合は、繰り上がりではなく、桁借り(ボロー)が発生したか否か、となる。ボローが発生した場合はそれを次の減算でさらにボローのぶんを引くことで上の桁の計算に反映していく。アーキテクチャにより、ボローの情報を示すボローフラグがキャリーフラグと別にあるもの、キャリーフラグとボローフラグが兼用で、加算の時にはキャリーフラグがキャリーを示し減算の時にはキャリーフラグがボローを示す、といったものがある。
後者にはさらに、ボローが発生した時にフラグが立つものと、逆のものがある。なぜ逆のものがあるかというと、加算器で2の補数を使って減算を行った場合(加算器#減算器も参照)その加算器のキャリー入出力をそのまま扱うとそうなるためである。逆になっているプロセッサとしては、6502・POWER・ARM・PICなどがある。
逆になっていても、長桁加算が「ADD → ADD with-carry」で行えるのと同様、普通は単に「SUB → SUB with-borrow」で計算すれば辻褄が合うように設計されているのが普通である。
オーバーフロー
テンプレート:Seealso 一方オーバフローは「あふれ」という意味である。これは符号付数値の場合に意味を持つ。4ビットで表せる符合付整数は −8 から 7 までである。そこでたとえば 7 に 1 を加算すると次のようになる。
0111 + 0001 = 1000
このようになり、符号付整数として解釈した場合、−8 という答えになる。これはもともと 8 という答えを4ビットの符号付整数では表せないために発生するもので、このようなときにオーバフローフラグがセットされる。これを符号なし整数と解釈すれば桁あふれは起きていないので、この演算ではキャリーフラグはセットされない。
最上位桁への繰り上がりや繰り下がりがあり、かつ同時に、最上位桁より上の桁への繰り上がりや繰り下がりが無い時がオーバーフローである。
なお、プロセッサのビット幅以上の符号付整数の演算を行う場合は、最上位桁だけ符号付と解釈して、それ以外は符号なしと解釈する(すなわち、オーバフローフラグではなくキャリーフラグを桁上がりに使う)ことで実現できる。