転送命令
レジスタ間、レジスタ・メモリ間でデータをコピーする命令を転送命令といいます。プログラム中で最もよく使われる命令です。ほかのCPU ではレジスタに格納された値をメモリにコピーすることをストア、メモリに格納された値をレジスタにコピーすることをロードと呼ぶ場合が多いですが、x86-64ではどちらもムーブ(move)と呼んで、MOV というオペコード (opcode:operation code) を使います。転送の対象となるレジスタやメモリアドレスをオペランド(oprand:被演算子)と呼び、オペコードの後ろに記述してます。CやJavaの関数名がオペコード、関数の引数がオペランドと考えれば大丈夫です。
「データをコピーする命令が最もよく使われる」というのは何度も同じものを生成するようで無駄な気もします。よく使われる理由となるのが汎用レジスタの数が有限であることです。汎用レジスタの数は多く持っているCPUでも 32 本、x86-64では16本、32ビットモードのx86ではたったの 8 本です。C、Java、Pascal などの高級言語でローカル変数を使わず、グローバル変数を8個だけ使ってプログラムを作成することを考えてみてください。さらに、関数(手続き)の引数も使えず、グローバル変数を使って渡す必要があります。ただし、グローバルなスコープを持つバイト(8ビット)型の大きな配列 Memory[]を使うことはできるとします。すると、変数間のコピーや配列 Memory とグローバル変数間のコピーを駆使してプログラムを作ることになります。アセンブリ言語で転送命令が重要な理由を感じ取っていただけますか?
MOV
x86-64 の転送命令は以下のようなものがあります。非常に種類が多いようですが、 ubuntu-8.10-desktop-amd64 の 64ビット版の bash-3.2.39 と gimp-2.6.1 で使用している命令の出現頻度のうち mov 命令 (約35万命令) の出現頻度を表の右端に示しています。 コンパイラ(gcc)が生成しない命令は優先順位が低いと考えればいいと思います。
転送命令のフォーマットを下の表に示します。 8 ビットレジスタを使用する転送命令では、64ビットモードで新規に追加された8ビットレジスタ (DIL, SIL, BPL, SPL, R8B - R15B)とAH、BH、CH、DHレジスタは 1 命令中で共存できません (REXプリフィックスが付くとAH、BH、CH、DHは指定できないため)。
表記 : MOV OP1, OP2 動作 : OP1 = OP2; 例 : MOV rax, 0x200
プリフィックス | オペコード | 命令 | 説明 | 出現頻度 |
---|---|---|---|---|
- | 88/r | MOV r/m8, r8 | r8 を r/m8 にコピー | 903 |
REX | MOV r/m8, r8 | r8 を r/m8 にコピー。 | 568 | |
66 | 89/r | MOV r/m16, r16 | r16 を r/m16 にコピー | 45 |
- | MOV r/m32, r32 | r32 を r/m32 にコピー | 14014 | |
REX.W | MOV r/m64, r64 | r64 を r/m64 にコピー | 166219 | |
- | 8A/r | MOV r8, r/m8 | r/m8 を r8 にコピー | 2 |
REX | MOV r8, r/m8 | r/m8 を r8 にコピー。 | 0 | |
66 | 8B/r | MOV r16, r/m16 | r/m16 を r16 にコピー | 0 |
- | MOV r32, r/m32 | r/m32 を r32 にコピー | 12481 | |
REX.W | MOV r64, r/m64 | r/m64 を r64 にコピー | 75929 | |
- | 8C/r | MOV r/m16, Sreg | セグメントレジスタを r/m16 にコピー | 0 |
REX.W | MOV r/m64, Sreg | ゼロ拡張された16ビットのセグメントレジスタ を r/m64 にコピー | 0 | |
- | 8E/r | MOV Sreg, r/m16 | r/m16 をセグメントレジスタにコピー | 0 |
REX.W | MOV Sreg, r/m64 | r/m64 の下位16ビットをセグメントレジスタにコピー | 0 | |
- | A0 | MOV AL, moffs8 | (セグメント:オフセット) のバイトを AL にコピー | 0 |
REX.W | MOV AL, moffs8 | (オフセット) のバイトを AL にコピー | 0 | |
66 | A1 | MOV AX, moffs16 | (セグメント:オフセット) のワードを AX にコピー | 0 |
- | MOV EAX, moffs32 | (セグメント:オフセット) のダブルワードを EAX にコピー | 0 | |
REX.W | MOV RAX, moffs64 | (オフセット) のクワッドワードを RAX にコピー | 0 | |
- | A2 | MOV moffs8, AL | AL を(セグメント:オフセット) にコピー | 0 |
REX.W | MOV moffs8, AL | AL を(オフセット) にコピー | 0 | |
- | A3 | MOV moffs16, AX | AX を(セグメント:オフセット) にコピー | 0 |
REX.W | MOV moffs64, RAX | RAX を(オフセット) にコピー | 0 | |
- | B0+rb | MOV r8, imm8 | 8ビット定数 を r8 にコピー | 56 |
REX | MOV r8, imm8 | 8ビット定数 を r8 にコピー。 | 24 | |
66 | B8+rw | MOV r16, imm16 | 16ビット定数 を r16 にコピー | 9 |
- | B8+rd | MOV r32, imm32 | 32ビット定数 を r32 にコピー | 58359 |
REX.W | MOV r64, imm64 | 64ビット定数 を r64 にコピー | 7254 | |
- | C6/0 | MOV r/m8, imm8 | 8ビット定数 を r/m8 にコピー | 951 |
REX | MOV r/m8, imm8 | 8ビット定数 を r/m8 にコピー。 | 199 | |
66 | C7/0 | MOV r/m16, imm16 | 16ビット定数 を r/m16 にコピー | 83 |
- | MOV r/m32, imm32 | 32ビット定数 を r/m32 にコピー | 6248 | |
REX.W | MOV r/m64, imm32 | 64ビットにゼロ拡張された 32ビット定数 を r/m64 にコピー | 9102 |
フラグの変化 : 変化しない。
フラグ | OF | SF | ZF | AF | CF | PF |
---|---|---|---|---|---|---|
実行後 | - | - | - | - | - | - |
CBW, CWDE, CDQE
AL、AX、EAXを倍の長さのレジスタに符号拡張する命令です。
表記 : CQDE 動作 : OP1 = OP1; 例 : CQDE
プリフィックス | オペコード | 命令 | 説明 |
---|---|---|---|
66 | 98 | CBW | AL を符号拡張して AX に設定 |
- | 98 | CWDE | AX を符号拡張して EAX に設定 |
REX.W | 98 | CDQE | EAX を符号拡張して RAX に設定 |
フラグの変化 : 変化しない。
フラグ | OF | SF | ZF | AF | CF | PF |
---|---|---|---|---|---|---|
実行後 | - | - | - | - | - | - |
CWD, CDQ, CQO
AX、EAX、RAXをDX:AX、EDX:EAX、RDX:RAX レジスタに符号拡張して転送する命令です。 除算の前に rDX レジスタを設定する場合に使用します。
表記 : CQO 動作 : EDX:EAX = EAX; 例 : CQO
プリフィックス | オペコード | 命令 | 説明 |
---|---|---|---|
66 | 99 | CWD | AL を符号拡張して DX:AX に設定 |
- | 99 | CDQ | EAX を符号拡張して EDX:EAX に設定 |
REX.W | 99 | CQO | RAX を符号拡張して RDX:RAX に設定 |
フラグの変化 : 変化しない。
フラグ | OF | SF | ZF | AF | CF | PF |
---|---|---|---|---|---|---|
実行後 | - | - | - | - | - | - |
MOVSX
レジスタ、またはメモリをより大きなサイズ(たとえば 8 ビットから 64 ビット)に符号拡張する命令です。 64ビットモードで新規に追加された8ビットレジスタ(DIL, SIL, BPL, SPL, R8B - R15B)とAH、BH、CH、DH レジスタは 1 命令中で共存できません(REXプリフィックスが付くとAH、BH、CH、DHは指定できないため)。
表記 : MOVSX OP1, OP2 動作 : OP1 = OP2; 例 : MOVSX rax, al
プリフィックス | オペコード | 命令 | 説明 |
---|---|---|---|
66 | 0F BE /r | MOVSX r16, r/m8 | バイトをワードに符号拡張して転送する。 |
- | 0F BE /r | MOVSX r32, r/m8 | バイトをダブルワードに符号拡張して転送する。 |
REX | 0F BE /r | MOVSX r64, r/m8 | バイトをクワッドワードに符号拡張して転送する。 |
- | 0F BF /r | MOVSX r32, r/m16 | ワードをダブルワードに符号拡張して転送する。 |
REX.W | 0F BF /r | MOVSX r64, r/m16 | ワードをクワッドワードに符号拡張して転送する。 |
REX.W | 63 /r | MOVSXD r64,r/m32 | ダブルワードをクワッドワードに符号拡張して転送する。 |
フラグの変化 : 変化しない。
フラグ | OF | SF | ZF | AF | CF | PF |
---|---|---|---|---|---|---|
実行後 | - | - | - | - | - | - |
MOVZX
レジスタ、またはメモリをより大きなサイズ(たとえば 8 ビットから 64 ビット)にゼロ拡張する命令です。 64ビットモードで新規に追加された8ビットレジスタ(DIL, SIL, BPL, SPL, R8B - R15B)とAH、BH、CH、DH レジスタは 1 命令中で共存できません(REXプリフィックスが付くとAH、BH、CH、DHは指定できないため)。
表記 : MOVZX OP1, OP2 動作 : OP1 = OP2; 例 : MOVZX rax, al
プリフィックス | オペコード | 命令 | 説明 |
---|---|---|---|
66 | 0F B6 /r | MOVZX r16,r/m8 | バイトをワードにゼロ拡張して転送する。 |
- | 0F B6 /r | MOVZX r32,r/m8 | バイトをダブルワードにゼロ拡張して転送する。 |
REX | 0F B6 /r | MOVZX r64,r/m8 | バイトをクワッドワードにゼロ拡張して転送する。 |
- | 0F B7 /r | MOVZX r32,r/m16 | ワードをダブルワードにゼロ拡張して転送する。 |
REX.W | 0F B7 /r | MOVZX r64,r/m32 | ダブルワードをクワッドワードにゼロ拡張して転送する。 |
フラグの変化 : 変化しない。
フラグ | OF | SF | ZF | AF | CF | PF |
---|---|---|---|---|---|---|
実行後 | - | - | - | - | - | - |
LEA
実効アドレスをレジスタに設定します。MOV命令では指定メモリアドレス(実効アドレス)の内容をレジスタに転送しますが、LEAはメモリの内容ではなくアドレスの値そのものをレジスタに設定します。 アドレッシングモードで説明した SIB を利用すると演算に利用できます。実効アドレスのビット数よりレジスタが小さい場合は上位ビットがレジスタの長さまで切り捨てられます。
表記 : LEA OP1, [OP2] 動作 : OP1 = &OP2; 例 : LEA rax, [rax + rax * 4] ; rax = rax * 5
プリフィックス | オペコード | 命令 | 説明 |
---|---|---|---|
66 | 8D /r | LEA r16, m | m の実効アドレスをレジスタr16 に格納する。 |
- | 8D /r | LEA r32, m | m の実効アドレスをレジスタr32 に格納する。 |
LEX.W | 8D /r | LEA r64, m | m の実効アドレスをレジスタr64 に格納する。 |
フラグの変化 : 変化しない。
フラグ | OF | SF | ZF | AF | CF | PF |
---|---|---|---|---|---|---|
実行後 | - | - | - | - | - | - |
BSWAP
レジスタの内容のバイト順序を逆にする命令です。リトルエンディアンをビッグエンディアンに変更、またはその逆の動作をします。
表記 : BSWAP OP1 例 : BSWAP rax
プリフィックス | オペコード | 命令 | 説明 |
---|---|---|---|
- | 0F C8+rd | BSWAP r32 | 32 ビット・レジスタのバイト順序を逆にする。 |
REX.W | 0F C8+rd | BSWAP r64 | 64 ビット・レジスタのバイト順序を逆にする。 |
フラグの変化 : 変化しない。
フラグ | OF | SF | ZF | AF | CF | PF |
---|---|---|---|---|---|---|
実行後 | - | - | - | - | - | - |
XCHG
オペランドの2つのレジスタ間、またはレジスタとメモリ間で値を交換します。 メモリをアクセスする場合は CPU の Lock signal をアクティブにします。 "XCHG EAX, EAX" (0x90) は何もしない命令であるNOP命令としても使われます。
表記 : XCHG OP1, OP2 動作 : tmp = OP1; OP1 = OP2; OP2 = tmp; 例 : XCHG rax, rbx
プリフィックス | オペコード | 命令 | 説明 |
---|---|---|---|
66 | 90+rw | XCHG AX, r16 | r16 をAX と交換する。 |
66 | 90+rw | XCHG r16, AX | AX をr16 と交換する。 |
- | 90+rd | XCHG EAX, r32 | r32 をEAX と交換する。 |
- | 90+rd | XCHG r32, EAX | EAX をr32 と交換する。 |
REX.W | 90+rd | XCHG RAX, r64 | r64 をRAX と交換する。 |
REX.W | 90+rd | XCHG r64, RAX | RAX をr64 と交換する。 |
- | 86 /r | XCHG r/m8, r8 | r8(バイトレジスタ)をr/m8 からのバイトと交換する。 |
REX | 86 /r | XCHG r/m8, r8 | r8(バイトレジスタ)をr/m8 からのバイトと交換する。 |
- | 86 /r | XCHG r8, r/m8 | r/m8 からのバイトをr8(バイトレジスタ)と交換する。 |
REX | 86 /r | XCHG r8, r/m8 | r/m8 からのバイトをr8(バイトレジスタ)と交換する。 |
66 | 87 /r | XCHG r/m16, r16 | r16 をr/m16 からのワードと交換する。 |
66 | 87 /r | XCHG r16, r/m16 | r/m16 からのワードをr16 と交換する。 |
- | 87 /r | XCHG r/m32, r32 | r32 をr/m32 からのダブルワードと交換する。 |
REX.W | 87 /r | XCHG r/m64, r64 | r64 をr/m64 からのクワッドワードと交換する。 |
- | 87 /r | XCHG r32, r/m32 | r/m32 からのダブルワードをr32 と交換する。 |
REX.W | 87 /r | XCHG r64, r/m64 | r/m64 からのクワッドワードをr64 と交換する。 |
フラグの変化 : 変化しない。
フラグ | OF | SF | ZF | AF | CF | PF |
---|---|---|---|---|---|---|
実行後 | - | - | - | - | - | - |
何もしない命令
実行しても何も起こりません。アラインメントを合わせる場合や場所を確保する場合に使用します。 オペコードは0x90になっていて、XCHG EAX, EAX という命令が使われています。
表記 : NOP 動作 : 何もしない 例 : NOP NOP rax
プリフィックス | オペコード | 命令 | 説明 |
---|---|---|---|
- | 90 | NOP | 1 バイトの何もしない命令 |
- | 0F 1F /0 | NOP r/m32 | 3 バイトの何もしない命令 |
「nop eax」の形式のアドレッシングを工夫することで、1命令で複数バイトの場所を確保することができます。
命令長 | バイトシーケンス | NASM表現 |
---|---|---|
1 | 90 | nop |
3 | 0F1FC0 | nop eax |
4 | 480F1FC0 | nop rax |
5 | 670F1F4000 | nop dword [byte eax + 0x00] |
6 | 670F1F440000 | nop dword [byte eax + eax + 0x00] |
7 | 0F1F8000000000 | nop dword [dword rax + 0x00000000] |
8 | 0F1F840000000000 | nop dword [dword rax + rax + 0x00000000] |
9 | 670F1F840000000000 | nop dword [dword eax + eax + 0x00000000] |
10 | 67480F1F840000000000 | nop qword [dword eax + eax + 0x00000000] |
フラグの変化 : 変化しない。
フラグ | OF | SF | ZF | AF | CF | PF |
---|---|---|---|---|---|---|
実行後 | - | - | - | - | - | - |