転送命令

レジスタ間、レジスタ・メモリ間でデータをコピーする命令を転送命令といいます。プログラム中で最もよく使われる命令です。ほかの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
実行後 - - - - - -

続く...


このページの目次