アドレスを指定したデータの転送(Intel記法)

アドレスを指定したデータの転送についても、少しはわかってきた気がするので、まとめる。

動作確認のための数値

前提として、アセンブラの動作確認のため、1073606947825の値を用いた。 この数値を16進数に直すと0xF9F7F5F3F1であり、各バイト毎の値を順に変えている。 こうしておくと、一部のバイトのみ転送する場合に、どのバイトが転送されたのか確認しやすい。

また、この数では、8byte, 4byte 2byteを切り出したときに最上位bitが1になるので、 ゼロ拡張されるのか、符号拡張されるのかを確認しやすい。

レジスタからアドレスへの転送の基本

レジスタraxの指す値へbyte数を指定して転送する。 ちなみにbyte ptrは1byteのみ転送、word ptrは2byteのみ転送、dword ptrは4byteのみ転送、qword ptrは8byteすべて転送する(指定しないのと同様)。

数値によって、即値で転送できる場合とできない場合(0xF9F7F5F3F1はqword ptrには即値で転送できなかったが、qword ptrにも0などの即値なら転送できた。)があったので、rdiに即値を転送してから、rdiから[rax]へ転送するようにした。

  # 0xF9F7F5F3F1
  mov rdi, 1073606947825

  # raxからのアドレス1byteに、0x00000000000000F1を転送する
  mov byte ptr [rax], dil

  # raxからのアドレス2byteの範囲に、0x000000000000F3F1を転送する
  mov word ptr [rax], di # 0xF9F7F5F3F1

  # raxからのアドレス4byteの範囲に、0x00000000F7F5F3F1を転送する
  mov dword ptr [rax], edi

  # raxからのアドレス8byteの範囲に、0x000000F9F7F5F3F1を転送する
  mov qword ptr [rax], rdi

アドレスの確認

転送先のoffsetを変えて、アドレスの起点を確認してみた。

rax+1すると、raxに格納される値は左に1byte分シフトした。

  # 0xF9F7F5F3F1
  mov rdi, 1073606947825

  # raxのアドレスから8byteは、0x0000000000000000になる。
  mov byte ptr [rax-1], dil

  # raxのアドレスから8byteは、0x00000000000000F3になる。
  mov word ptr [rax-1], di # 0xF9F7F5F3F1

  # raxのアドレスから8byteは、0x000000000000F5F3になる。
  mov dword ptr [rax-1], edi

  # raxのアドレスから8byteは、0x00000000F9F7F5F3になる。
  mov qword ptr [rax-1], rdi

rax-1すると、raxに格納される値は右に1byte分シフトした。

  # 0xF9F7F5F3F1
  mov rdi, 1073606947825

  # raxのアドレスから8byteは、0x000000000000F100になる。
  mov byte ptr [rax+1], dil

  # raxのアドレスから8byteは、0x0000000000F2F100になる。
  mov word ptr [rax+1], di # 0xF9F7F5F3F1

  # raxのアドレスから8byteは、0x000000F7F5F3F100になる。
  mov dword ptr [rax+1], edi

  # raxのアドレスから8byteは、0x0000F9F7F5F3F100になる。
  mov qword ptr [rax+1], rdi

ちなみに、sourceに指定したレジスタのサイズと転送先のレジスタのサイズは同じにする必要がある。 dword ptr [rax]なら、8byteレジスタのrdiではなく4byteレジスタのediにする必要がある。

そのため、転送先のサイズを指定せずに即値をアドレスに転送することもできない。 例えば以下はだめ。

  mov [rax], 0x000000F7F5F3F100

アドレスからレジスタへの転送

とりあえず転送の範囲などを確認するため、[rax]の値と、rsiの値を初期化しておく。

  # [rax]を0x000000F9F7F5F3F1としておく
  mov rdi, 1073606947825
  mov [rax], rdi

  # rsiを0xAAAAAAAAAAAAAAAAとしておく
  mov rsi, 12297829382473034410

12297829382473034410は16進数に直すと0xAAAAAAAAAAAAAAAAである。

1byteの転送を以下のコードで確認すると、 rsiの値は0xAAAAAAAAAAAAAAF1となった。 レジスタへの転送のときはptrを指定しなくても転送先のレジスタサイズのみ転送される。

  mov sil, [rax]

2byteの転送を以下のコードで確認すると、 rsiの値は0xAAAAAAAAAAAAF3F1となった。

  mov si, [rax]

4byteの転送を以下のコードで確認すると、 rsiの値は0x00000000F7F5F3F1となった。

  mov esi, [rax]

4byteの転送のみ、レジスタの上位ビットがゼロ拡張されている。 これはeaxにデータ転送したとき、上位4byteはゼロ拡張されるためである。

ちなみに、上位ビットを符号拡張したい場合はmovsxまたはmovsxd命令を使って、rsiまたはesiに転送すればよい。 rsiに転送すれば、8byteの範囲全体に符号拡張し、esiに転送すれば4byteの範囲のみ符号拡張する。 符号拡張する場合は、movによる転送と異なり、転送先のサイズは符号拡張後サイズになるので、転送するサイズはptrで指定する必要がある。

8byteに転送して符号拡張するときは以下

  # 2byteを8byteに転送して符号拡張する 0xFFFFFFFFFFFFFFF1
  movsx rsi, byte ptr [rax]

  # 2byteを8byteに転送して符号拡張する 0xFFFFFFFFFFFFF3F1
  movsx rsi, word ptr [rax]

  # 4byteを8byteに転送して符号拡張する 0xFFFFFFFFF7F5F3F1
  movsx rsi, dword ptr [rax]

4byteに転送して符号拡張するときは以下

  # 2byteを4byteに転送して符号拡張する 0x00000000FFFFFFF1
  movsx esi, byte ptr [rax]

  # 2byteを4byteに転送して符号拡張する 0x00000000FFFFF3F1
  movsx esi, word ptr [rax]

8byteに転送してゼロ拡張するときは以下。転送先をrsiにしても1byteと2byteは動作した。 4byteをゼロ拡張するときは、movでesiに転送すれば勝手に上位4byteはゼロ拡張というかゼロに初期化される。 4byteだけゼロ拡張というのは試した範囲ではできなかったが、、、

  # 1byteを4byteに転送してゼロ拡張する 0x00000000000000F1
  movzx esi, byte ptr [rax]

  # 2byteを8byteに転送してゼロ拡張する 0x000000000000F3F1
  movzx esi, word ptr [rax]

  # 4byteを8byteに転送してゼロ拡張する 0xFFFFFFFFF7F5F3F1
  mov esi, [rax]