x86の汎用レジスタ
おことわり: これらのレジスタは厳密には「汎用」とは言えない面もありますが、便宜上汎用レジスタと呼称しています。x86に関して、この用法はIntelの文書でも確認されています。
8008
8008はx86の源流となるプロセッサのひとつで、オペランド長は8bit, アドレス長は14bitです。汎用レジスタに相当するレジスタはA, B, C, D, E, H, Lの7本あります。このうちA, H, Lには以下の役割があります。
- A: アキュムレータ。デフォルトオペランドとして使用。
- H/L: メモリの上位アドレスと下位アドレス。
7つしかないのは、メモリオペランドを指定できるようにするためです。命令フォーマット上はレジスタとメモリオペランドが以下のように番号づけられています。
番号 | オペランド |
---|---|
0 (000) | A |
1 (001) | B |
2 (010) | C |
3 (011) | D |
4 (100) | E |
5 (101) | H |
6 (110) | L |
7 (111) | M = [H:L] |
8008のアセンブリ言語には大きく分けて旧ニーモニックと新ニーモニックの2種類があったようです。たとえば旧ニーモニックでADMという命令は新ニーモニックではADD Mと表記されます。
8080 / 8085
8080 / 8085は8008の後継にあたるプロセッサです。
命令セットアーキテクチャの観点からは、8008と比べて以下のような変更が加えられています。
- コールスタックのためのリング状のレジスタが廃止され、コールスタックはメモリ上に保存されるようになった。
- 命令フォーマットの整理
- IN命令、OUT命令が2バイト命令になった。
- opcodeが再配置された。
- IN/OUT命令を長くしたことで空いた空間に新規の命令が追加された。これには以下が含まれる。
- PUSH/POP系命令
- レジスタペアを扱う命令
このような変更点はあるものの、命令セットの構造は大きくは変更されていませんでした。特に、8008の新ニーモニックと8080のIntelニーモニックを比較すると、8008のアセンブリ命令はほぼそのまま8080の命令として解釈できることがわかります。[1]
このうちスタック置き場が変わったCALL/RET系命令を除くと命令の意味論もおおむね変化はなく、総じて「8008と8080はソースレベル互換性があった」と言ってよさそうです。
ただし、命令フォーマットは整理されたため、バイナリレベルの互換性はありません。特にレジスタの番号づけは以下のように変更されました。
番号 | オペランド |
---|---|
0 (000) | B |
1 (001) | C |
2 (010) | D |
3 (011) | E |
4 (100) | H |
5 (101) | L |
6 (110) | M = [H:L] |
7 (111) | A |
この変更はおそらくレジスタペアの導入に関係しています。レジスタペアは以下の4つの対と1つの特別なレジスタからなります。
番号 | オペランド | 備考 |
---|---|---|
0 (00) | B:C | 単に B ともいう |
1 (01) | D:E | 単に D ともいう |
2 (10) | H:L | 単に H ともいう |
3 (11) | A:F | PSW と呼ばれる Fはフラグレジスタ PUSH, POP命令で利用可能 |
3 (11) | SP | LXI, DAD, INX, DCX命令で利用可能 |
レジスタペアの番号が自然に計算できるように、レジスタの番号が1ずつ動かされたものと考えられます。
8080ではレジスタペアに対して限定的な演算を行うことが可能でした。これを踏まえると、8080は16bit CPUとしての性質も部分的に持っていたと言えそうです。
Z80
Z80はZilog社によるプロセッサで、8080の機械語がほぼそのまま動くような形で実装されています。
ただし、Z80向けのアセンブリはIntelニーモニックとは異なるZilogニーモニックで記述されていたようです。8080までのIntelニーモニック分類が機械語の構造を反映したものだったのに対し、Zilogニーモニックは実行時の意味論に基づいて分類されています。たとえば、Intelニーモニックにおける ADD (A ← A + reg) と ADI (A ← A + imm) はZilogニーモニックでは ADD に統一されていました。後述する8086のアセンブリ(Intel記法)はこの方式に近く、Zilogニーモニックの影響を受けたのかもしれません。
汎用レジスターの観点からは、Z80の以下の拡張が特筆に値します。
- IX, IY レジスターの追加。
-
EX AF, AF'
命令とEXX
命令によるレジスタ退避領域の追加。
このうちIX, IYは、ベースオフセット方式のアドレッシングモードのサポートとともに追加されていました。 DD と FD がそれぞれ IX, IY 用のプレフィックスとして使われ、これらの接頭辞がある場合は以下のように命令の解釈が変更されます。
- DDがついている場合
-
HL
オペランドがIX
オペランドとして再解釈される -
(HL)
オペランドが(IX+d)
オペランドとして再解釈される
-
- FDがついている場合
-
HL
オペランドがIX
オペランドとして再解釈される -
(HL)
オペランドが(IX+d)
オペランドとして再解釈される
-
番号 | オペランド |
---|---|
0 (000) | B |
1 (001) | C |
2 (010) | D |
3 (011) | E |
4 (100) | H |
5 (101) | L |
6 (110) | (HL) |
6 (110) | (IX+d) |
6 (110) | (IY+d) |
7 (111) | A |
番号 | オペランド | 備考 |
---|---|---|
0 (00) | BC | |
1 (01) | DE | |
2 (10) | HL | |
2 (10) | IX | DDプレフィックス使用時 |
2 (10) | IY | FDプレフィックス使用時 |
3 (11) | AF | PUSH, POP, EX AF, AF' 命令で利用可能 |
3 (11) | SP |
8086 / 8088 / 80186 / 80188
8086は8080の後継にあたる16bitプロセッサであり、現代のx86 CPUはこの8086との互換性を保っています[2]。オペランド長は16bit、論理アドレス長は32bit (16bitセグメント + 16bit線形アドレス)、物理アドレス長は20bitです。
8086には8080とのソースレベル・バイナリレベルの互換性はありませんが、8080アセンブリからのソース・ソース変換により容易に移植できるように設計されています。そのようなツールとしてIntel公式のCONV-86や、MS-DOSに同梱されたTRANS-86などがあったようです。
なお、x86のアセンブリ言語は大きくIntel記法とAT&T記法の2種類が知られています。AT&T記法はオペランド長をニーモニックに含める点と、オペランド順がPDP-11に倣う形になっていてIntel記法と真逆になっている点が特徴です。本稿ではIntel記法を使います。
8086の汎用レジスタは8本あり、以下のように番号づけられています。
番号 | レジスタ |
---|---|
0 (000) | ax = ah:al |
1 (001) | cx = ch:cl |
2 (010) | dx = dh:dl |
3 (011) | bx = bh:bl |
4 (100) | sp |
5 (101) | bp |
6 (110) | si |
7 (111) | di |
これらのレジスタには以下のような役割があります。
- ax は拡張アキュムレータ。一部の演算命令のデフォルトオペランド。
- cx は拡張カウンタ。一部の繰り返し命令のカウンタ。
- dx は拡張データ。
- bx は拡張ベース。メモリオペランドのベースレジスタとして利用可能。
- sp はスタックポインタ。push/popの操作対象。
- bp はベースポインタ (直前のスタックフレーム)。メモリオペランドのベースレジスタとして利用可能。
- si はソースインデックス。メモリオペランドのインデックスレジスタとして利用可能。一部の文字列命令のデフォルトオペランド。
- di は宛先インデックス。メモリオペランドのインデックスレジスタとして利用可能。一部の文字列命令のデフォルトオペランド。
ax, cx, dx, bx の4つはそれぞれ上位バイトと下位バイトに分割して8bitレジスタとして参照することもできます。このときの番号づけは以下の通りです。
番号 | レジスタ |
---|---|
0 (000) | al |
1 (001) | cl |
2 (010) | dl |
3 (011) | bl |
4 (100) | ah |
5 (101) | ch |
6 (110) | dh |
7 (111) | bh |
実は、8086の汎用レジスタはリリース前はxa, bc, de, hl, sp, mp, ij, ikという名前がつけられていました。つまり、8080と8086の汎用レジスタには以下のような対応関係が想定されていたことがわかります。
- A = al
- B = ch, C = cl, B:C = cx
- D = dh, E = dl, D:E = dx
- H = bh, L = bl, H:L = bx
これをさらに確かめるにはIntelが提供していた変換プログラム (CONV-86) を参照するのが良さそうですが、これは入手が簡単ではなさそうなので、ここではかわりに TRANS.COM の挙動を参照します。 TRANS.COM はMS-DOSのオリジナルの開発元であるSeattle Computer Products社製のツールで、現在はMS-DOSの一部としてオープンソース化されているため、ソースを読んで挙動を理解することができます。
TRANS.COMは8080からではなく、Z80からの移行を想定して作られていました。ここでのレジスタの対応は以下の通りです。
Z80 | 8086 | ||
---|---|---|---|
3: AF (PSW) |
0: AX | ※ | |
7: A | 0: AL | ||
0: BC | 1: CX | ||
0: B | 5: CH | ||
1: C | 1: CL | ||
1: DE | 2: DX | ||
2: D | 6: DH | ||
3: E | 2: DL | ||
2: HL | 3: BX | ||
4: H | 7: BH | ||
5: L | 3: BL | ||
3: SP | 4: SP | ||
2: IX | 6: SI | ※ | |
2: IY | 7: DI | ※ |
ただし、
- レジスタペア AF (PSW) は AX に対応しますが、これをそのまま解釈すると A = AH, F = FL になります。しかし実際には、8bitレジスタとして使うときは A = AL という対応関係が想定されていました。
- AF (PSW) が使われる命令は
EX AF, AF'
,PUSH AF
,POP AF
の3種類しかありません。そのため、普段は A = AL の対応関係を保っておき、これらの3命令に対応する操作を行いたいときだけLAHF
,XCHG AH, AL
,SAHF
を使って一時的に AF = AX の関係を確立する仕組みでした。
- AF (PSW) が使われる命令は
- IX/IYに対応する値は普段はメモリ上に保管しておき、間接アドレッシングが必要なときだけSI/DIに読み出す仕組みでした。
- AFの裏レジスタはBPに対応づけられましたが、このときバイト順は本来のAFとは逆順で保管されます。また、BC, DE, HLの裏レジスタはメモリ上に保管する仕組みでした。
80286
80286は8086の後継にあたる上位互換のプロセッサで、プロテクテッドモードの導入という非常に大きな変化がもたらされました。ただし、汎用レジスタという観点からは大きな変更はないため、本稿では大きくは扱いません。
80386 (i386)
80386は80286の後継にあたる上位互換のプロセッサで、32bit対応とページングの導入が果たされました。
32bit対応により汎用レジスタも拡張されました。既存の16bitレジスタが上位方向に伸長され、これらには "e" を前置する規則的な命名が与えられました。既存のレジスタ名は、新しい32bitレジスタの下位ビットのエイリアスと位置づけられました。
32bit | 16bit | 8bit(上位) | 8bit(下位) |
---|---|---|---|
0: eax | 0: ax | 4: ah | 0: al |
1: ecx | 1: cx | 5: ch | 1: cl |
2: edx | 2: dx | 6: dh | 2: dl |
3: ebx | 3: bx | 7: bh | 3: bl |
4: esp | 4: sp | ||
5: ebp | 5: bp | ||
6: esi | 6: si | ||
7: edi | 7: di |
レジスタごとの特別な役割は、16bitの場合と大きくは変わりません。しかし、32bitアドレッシングではアドレッシングモードが以下のように拡張されており、bx/bp/si/diレジスタの特殊性はやや薄れています。
base | index | scale | disp | |
---|---|---|---|---|
16bit | bx, bp または無し | si, di または無し | 1 | 16bit |
32bit | eax, ecx, edx, ebx, esp, ebp, esi, edi または無し |
eax, ecx, edx, ebx, ebp, esi, edi または無し |
1, 2, 4, 8 | 32bit |
x86-64
時代は下り、IA-64を頑張っていたIntelに代わってAMDがx86の64bit化を行いました。64bitモードでは様々な変更点がありますが、汎用レジスタの観点からは以下の3点が特筆に値します。
- レジスタが64bitに拡張された。
- レジスタが倍増した。
- 上記とは独立に、8bitレジスタが4つ追加された。
これらをまとめると以下のようになります。
64bit | 32bit | 16bit | 8bit(上位) | 8bit(下位) |
---|---|---|---|---|
0: rax | 0: eax | 0: ax | 4: ah | 0: al |
1: rcx | 1: ecx | 1: cx | 5: ch | 1: cl |
2: rdx | 2: edx | 2: dx | 6: dh | 2: dl |
3: rbx | 3: ebx | 3: bx | 7: bh | 3: bl |
4: rsp | 4: esp | 4: sp | 4: spl | |
5: rbp | 5: ebp | 5: bp | 5: bpl | |
6: rsi | 6: esi | 6: si | 6: sil | |
7: rdi | 7: edi | 7: di | 7: dil | |
8: r8 | 8: r8d | 8: r8w | 8: r8b | |
9: r9 | 9: r9d | 9: r9w | 9: r9b | |
10: r10 | 10: r10d | 10: r10w | 10: r10b | |
11: r11 | 11: r11d | 11: r11w | 11: r11b | |
12: r12 | 12: r12d | 12: r12w | 12: r12b | |
13: r13 | 13: r13d | 13: r13w | 13: r13b | |
14: r14 | 14: r14d | 14: r14w | 14: r14b | |
15: r15 | 15: r15d | 15: r15w | 15: r15b |
まず、eax, ecx, ... をさらに上位ビットに拡張した rax, rcx, ... というレジスタが追加されました。以前の短いレジスタは、読み取り時は64bitレジスタの下位ビットを参照するエイリアスとして機能します。いっぽう、書き込み時は以下のような挙動になります。
- 32bitレジスタ: ゼロ拡張して64bitレジスタ全体に書き込む。
- 8bit/16bitレジスタ: 64bitレジスタの上位ビットは保存し、下位ビットのみに書き込む。
これはおそらくパイプライン実行において余計なレジスタ読み取り依存を発生させないための対応ではないかと思います。
次に、レジスタが8個から16個に拡張されました。x86の命令フォーマットは8008以来伝統の 2bit + 3bit + 3bit 構造を保つMod R/Mバイトを持っており、これらによってレジスタの数は8個に制限されていました。AMDはREXという命令プレフィックスに1bitずつ追加の情報をエンコードすることで、これらの命令フォーマットを強引に拡張し、レジスタの数を倍増させることに成功しました。
最後に、8bitレジスタ spl, bpl, sil, dil が追加されました。REXプレフィックスが存在するときは、 ah, ch, dh, bh レジスタのかわりにこれらのレジスタが参照されます。これにより、8~32bitレジスタと64bitレジスタとの関係に一貫性がもたらされました。
既存の0~7番レジスタの役割に大きな変化はありません。アドレッシングモードも32bitモードのときとほぼ同じです。ただし、PC相対アドレッシング ([rip+DISP]
, [eip+DISP]
) が使えるようになったという違いはあり、ABIレベルでの設計には影響を与える可能性があります。
Intel APX
Intel APXはごく最近Intelが提案した最新の拡張です。過去のプロダクト名を考えるとだいぶ紛らわしい名前な気もします。
Intel APXはx86の命令の構造を覆す大きな変更といえます。これには以下が含まれます。
- 従来の2オペランドの汎用命令がRISC風の3オペランド形式に拡張される。
- 3オペランド形式では、8bit/16bitレジスタへの書き込み時も上位ビットがゼロクリアされる。
- 汎用レジスタがさらに倍増し、32個になる。
レジスタを増やす方法はx86-64導入時と同様で、新しい命令プレフィックスを使います。これにはEVEXという既存のSIMD用プレフィックスを汎用命令に転用するか、または新しいREX2プレフィックスを使うことになります。
これらをまとめると以下のようになります。
64bit | 32bit | 16bit | 8bit(上位) | 8bit(下位) |
---|---|---|---|---|
0: rax | 0: eax | 0: ax | 4: ah | 0: al |
1: rcx | 1: ecx | 1: cx | 5: ch | 1: cl |
2: rdx | 2: edx | 2: dx | 6: dh | 2: dl |
3: rbx | 3: ebx | 3: bx | 7: bh | 3: bl |
4: rsp | 4: esp | 4: sp | 4: spl | |
5: rbp | 5: ebp | 5: bp | 5: bpl | |
6: rsi | 6: esi | 6: si | 6: sil | |
7: rdi | 7: edi | 7: di | 7: dil | |
8: r8 | 8: r8d | 8: r8w | 8: r8b | |
9: r9 | 9: r9d | 9: r9w | 9: r9b | |
10: r10 | 10: r10d | 10: r10w | 10: r10b | |
11: r11 | 11: r11d | 11: r11w | 11: r11b | |
12: r12 | 12: r12d | 12: r12w | 12: r12b | |
13: r13 | 13: r13d | 13: r13w | 13: r13b | |
14: r14 | 14: r14d | 14: r14w | 14: r14b | |
15: r15 | 15: r15d | 15: r15w | 15: r15b | |
16: r16 | 16: r16d | 16: r16w | 16: r16b | |
17: r17 | 17: r17d | 17: r17w | 17: r17b | |
18: r18 | 18: r18d | 18: r18w | 18: r18b | |
19: r19 | 19: r19d | 19: r19w | 19: r19b | |
20: r20 | 20: r20d | 20: r20w | 20: r20b | |
21: r21 | 21: r21d | 21: r21w | 21: r21b | |
22: r22 | 22: r22d | 22: r22w | 22: r22b | |
23: r23 | 23: r23d | 23: r23w | 23: r23b | |
24: r24 | 24: r24d | 24: r24w | 24: r24b | |
25: r25 | 25: r25d | 25: r25w | 25: r25b | |
26: r26 | 26: r26d | 26: r26w | 26: r26b | |
27: r27 | 27: r27d | 27: r27w | 27: r27b | |
28: r28 | 28: r28d | 28: r28w | 28: r28b | |
29: r29 | 29: r29d | 29: r29w | 29: r29b | |
30: r30 | 30: r30d | 30: r30w | 30: r30b | |
31: r31 | 31: r31d | 31: r31w | 31: r31b |
APXによって追加されるr16~r31は、なるべく既存のr8~r15レジスタと同様の能力をもつように設計されていますが、例外もあります。現時点ではVEXプレフィックス付き命令(のうちEVEXの同等な形式を持たないもの)は拡張EVEX[3]プレフィックス命令に昇格できないため、これらの命令のうちGPRによるアドレッシングが可能なものはrax~r15までのレジスタしか利用できません。
4004 / 4040
4004はIntelの最初のプロセッサです。オペランド長は4bit, アドレス長は8bitです。
4004は8008の先祖にあたるとも考えられますが、命令セット上は隔たりも大きいため、本稿での言及は最小限に留めます。
おまけ: その他のナンバリング
フラグ
8008 | 8080 | Z80 | 8086 |
---|---|---|---|
C | 0: CY | 0: CY | 0: CF |
1: N | |||
P | 2: P | 2: P | 2: PF |
- | 4: AC | 4: AC | 4: AF |
Z | 6: Z | 6: Z | 6: ZF |
S | 7: S | 7: S | 7: SF |
- | - | 8: TF | |
- | - | 9: IF | |
- | - | 10: DF | |
- | - | 2: O | 11: OF |
2項演算
0ビット目と1ビット目が入れ替わっているのはともかく、ORとSUBが入れ替わっているのはかなり謎です。どういった事情があったのでしょうか。
8008 / 8080 / Z80 | 8086 | |
---|---|---|
ADD | 0 (000) | 0 (000) |
OR | 6 (110) | 1 (001) |
ADC | 1 (001) | 2 (010) |
SBB | 3 (011) | 3 (011) |
AND | 4 (100) | 4 (100) |
SUB | 2 (010) | 5 (101) |
XOR | 5 (101) | 6 (110) |
CMP | 7 (111) | 7 (111) |
条件コード
cc | 8008 | 8080 | Z80 | 8086 |
---|---|---|---|---|
OF=1 | 5: PE | 0: O | ||
OF=0 | 4: PO | 1: NO | ||
CF=1 | 4: C | 3: C | 3: C | 2: B/NAE/C |
CF=0 | 0: NC | 2: NC | 2: NC | 3: NB/AE/NC |
ZF=1 | 5: Z | 1: Z | 1: Z | 4: Z/E |
ZF=0 | 1: NZ | 0: NZ | 0: NZ | 5: NZ/NE |
CF=1 ∨ ZF=1 | 6: BE/NA | |||
CF=0 ∧ ZF=0 | 7: NBE/A | |||
SF=1 | 6: M | 7: M | 7: M | 8: S |
SF=0 | 2: P | 6: P | 6: P | 9: NS |
PF=1 | 7: PE | 5: PE | 5: PE | 10: P/PE |
PF=0 | 3: PO | 4: PO | 4: PO | 11: NP/PO |
SF xor OF | 12: L/NGE | |||
¬(SF xor OF) | 13: NL/GE | |||
ZF=1 ∨ (SF xor OF) | 14: LE/NG | |||
ZF=0 ∧ ¬(SF xor OF) | 15: NLE/G |
セグメントレジスタ
セグメントレジスタの番号はMOV命令 (8C, 8E) のreg fieldの解釈のほか、セグメントオーバーライドプレフィックスの並び順とも整合します。
0 | ES |
1 | CS |
2 | SS |
3 | DS |
4 | FS |
5 | GS |
文献
- Intel 8008 8-bit parallel central processor unit, April 1972
- 8080/8085 Assembly Language Programming Manual
- MCS-86 Assembly Language Reference Guide
- Z80 CPU User Manual
-
Intel® 64 and IA-32 Architecture Software Developer's Manual、特に:
- Volume 1: Basic Architecture, Chapter 3: Basic Execution Environment, 3.4 Basic Program Execution Registers
- Volume 1: Basic Architecture, Chapter 3: Basic Execution Environment, 3.4 Basic Program Execution Registers
-
Intel® Advanced Performance Extensions (Intel® APX) Architecture Specification, Revision 4.0、特に:
- 3.1.1 Introduction
- 3.1.2.4 Merge vs. Zero-Upper at the Destination Register
- 3.1.2.3.2 EVEX extension of VEX Instructions
Discussion