「X86」の版間の差分

提供: Wikinote
移動: 案内検索
(64 ビットモード)
(64 ビットモード)
 
(同じ利用者による、間の4版が非表示)
行20: 行20:
 
==== 64 ビットモード ====
 
==== 64 ビットモード ====
 
64 ビットモードでは、以下の 16 個の汎用レジスタが利用できる。
 
64 ビットモードでは、以下の 16 個の汎用レジスタが利用できる。
これらはそれぞれ、32 ビットでアクセスすることも可能だ。
+
これらはそれぞれ、下位 32 ビットのみアクセスすることも可能だ。
; 64 ビット
+
; 64 ビットアクセス
 
: RAX, RBX, RCX, RDX, RDI, RSI, RBP, RSP, R8 - R15
 
: RAX, RBX, RCX, RDX, RDI, RSI, RBP, RSP, R8 - R15
; 32 ビット
+
; 32 ビットアクセス
 
: EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP, R8D - R15D
 
: EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP, R8D - R15D
  
行30: 行30:
 
  [hagio@lab ~]$ cat > 64bit.c
 
  [hagio@lab ~]$ cat > 64bit.c
 
  int main(void) {
 
  int main(void) {
     long long int s64;
+
     long long int s64, r64;
 
     s64 = 0x1000000000000000LL;
 
     s64 = 0x1000000000000000LL;
 +
    r64 = s64 + 1;
 
     return 0;
 
     return 0;
 
  }
 
  }
  [hagio@lab ~]$ cc -S 64bit.c
+
  [hagio@lab ~]$ cc -S 64bit.c  
 
  [hagio@lab ~]$ cat 64bit.s
 
  [hagio@lab ~]$ cat 64bit.s
 
         .file  "64bit.c"
 
         .file  "64bit.c"
行48: 行49:
 
         pushl  %ecx
 
         pushl  %ecx
 
         subl    $20, %esp
 
         subl    $20, %esp
       ★movl    $0, -16(%ebp)
+
       ★movl    $0, -24(%ebp)
       ★movl    $268435456, -12(%ebp)
+
       ★movl    $268435456, -20(%ebp)
 +
        movl    -24(%ebp), %eax
 +
        movl    -20(%ebp), %edx
 +
      ☆addl    $1, %eax
 +
      ☆adcl    $0, %edx
 +
        movl    %eax, -16(%ebp)
 +
        movl    %edx, -12(%ebp)
 
         movl    $0, %eax
 
         movl    $0, %eax
 
         addl    $20, %esp
 
         addl    $20, %esp
行59: 行66:
 
         .ident  "GCC: (GNU) 4.1.2 20071124 (Red Hat 4.1.2-42)"
 
         .ident  "GCC: (GNU) 4.1.2 20071124 (Red Hat 4.1.2-42)"
 
         .section        .note.GNU-stack,"",@progbits
 
         .section        .note.GNU-stack,"",@progbits
単に 2 回に分けてメモリに格納しているようだ。64 ビット OS だとどうなるんだろう… 気になる。
+
おもろい。単に 4 バイトずつ 2 回に分けて計算しているだけのようだ。
 +
ただ、下位 4 バイトは下位 4 バイト同士普通に加算して、上位 4 バイトはキャリー付きの加算を行っている。
 +
つまり、下位 4 バイトの加算結果があふれたら、上位 4 バイトに 1 を足す。
 +
これにより、2 回に分けても上手く計算できるようになっている。おもろい!!
 +
乗算などの他の計算だとどうなるか… すでにやってみたのは言うまでもないが、余白がないので省略する。
 +
(ぜひいろいろ試してみてほしい…。)
 +
 
 +
64 ビットマシンだとどうなるのか気になる… (答えはわかるけどやらなきゃ気がすまない)。
  
 
=== セグメントレジスタ ===
 
=== セグメントレジスタ ===

2009年9月11日 (金) 21:16時点における最新版

そのうちアセンブリ言語も読まなきゃならなくなりそうなので。

参考文献

レジスタ

汎用レジスタ

これらは 32 ビットである。'E' を省いたものが下位 16 ビットを示す。 また、A - D までは、?L で下位 0-7 ビット、?H で 下位 8-15 ビットを示す。

  • EAX — Accumulator for operands and results data
  • EBX — Pointer to data in the DS segment
  • ECX — Counter for string and loop operations
  • EDX — I/O pointer
  • ESI — Pointer to data in the segment pointed to by the DS register; source pointer for string operations
  • EDI — Pointer to data (or destination) in the segment pointed to by the ES register; destination pointer for string operations
  • ESP — Stack pointer (in the SS segment)
  • EBP — Pointer to data on the stack (in the SS segment)

64 ビットモード

64 ビットモードでは、以下の 16 個の汎用レジスタが利用できる。 これらはそれぞれ、下位 32 ビットのみアクセスすることも可能だ。

64 ビットアクセス
RAX, RBX, RCX, RDX, RDI, RSI, RBP, RSP, R8 - R15
32 ビットアクセス
EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP, R8D - R15D

ところで、32 ビット OS でも long long int を定義すると 8 バイトの整数が定義できるが、 これはどのように処理されているのだろうか?実験してみた。

[hagio@lab ~]$ cat > 64bit.c
int main(void) {
    long long int s64, r64;
    s64 = 0x1000000000000000LL;
    r64 = s64 + 1;
    return 0;
}
[hagio@lab ~]$ cc -S 64bit.c 
[hagio@lab ~]$ cat 64bit.s
       .file   "64bit.c"
       .text
.globl main
       .type   main, @function
main:
       leal    4(%esp), %ecx
       andl    $-16, %esp
       pushl   -4(%ecx)
       pushl   %ebp
       movl    %esp, %ebp
       pushl   %ecx
       subl    $20, %esp
     ★movl    $0, -24(%ebp)
     ★movl    $268435456, -20(%ebp)
       movl    -24(%ebp), %eax
       movl    -20(%ebp), %edx
     ☆addl    $1, %eax
     ☆adcl    $0, %edx
       movl    %eax, -16(%ebp)
       movl    %edx, -12(%ebp)
       movl    $0, %eax
       addl    $20, %esp
       popl    %ecx
       popl    %ebp
       leal    -4(%ecx), %esp
       ret
       .size   main, .-main
       .ident  "GCC: (GNU) 4.1.2 20071124 (Red Hat 4.1.2-42)"
       .section        .note.GNU-stack,"",@progbits

おもろい。単に 4 バイトずつ 2 回に分けて計算しているだけのようだ。 ただ、下位 4 バイトは下位 4 バイト同士普通に加算して、上位 4 バイトはキャリー付きの加算を行っている。 つまり、下位 4 バイトの加算結果があふれたら、上位 4 バイトに 1 を足す。 これにより、2 回に分けても上手く計算できるようになっている。おもろい!! 乗算などの他の計算だとどうなるか… すでにやってみたのは言うまでもないが、余白がないので省略する。 (ぜひいろいろ試してみてほしい…。)

64 ビットマシンだとどうなるのか気になる… (答えはわかるけどやらなきゃ気がすまない)。

セグメントレジスタ

すべて 16 ビット幅である。

  • CS — Code segment register
  • DS, ES, FS, GS — Data segment register
  • SS — Stack segment register

フラグレジスタ

EFLAGS
32 ビット幅で、プロセッサ初期化後の値は 0x00000002 となる。

ステータスフラグ

  • CF (bit 0) — Carry flag
  • PF (bit 2) — Parity flag
  • AF (bit 4) — Adjust flag
  • ZF (bit 6) — Zero flag
  • SF (bit 7) — Sign flag
  • OF (bit 11) — Overflow flag

フラグは多すぎて面倒なのでまたやる気が出たら…

命令ポインタ

EIP (RIP)
32 ビット幅 (64 ビット幅)