x86のアドレッシングモードでハマった

なぜx86

大学の機械語序論という授業でx86(x86_64ではない)でアセンブラを書いています。

x86のアドレッシングモードが難しかったので復習を兼ねてまとめます。

やりたかったこと

授業でfib(10)を求めてebxに格納する課題がありました。

方針としては、aというシンボルのデータ領域を、アドレッシングモードを使って配列のように扱い、 フィボナッチ数列を計算することにしました。

わかりやすくRubyで書くとこんな方針です。

# fib.rb
a = [1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
(0..8).each {|i| a[i+2] = a[i+1] + a[i]}
puts a[10]

アセンブラでa[i+2]a[i+1]を参照する必要があったので、 アドレッシングモードを利用したいと思ったわけです。

x86のアドレッシングモード

授業の資料によると、x86 では、以下のアドレッシングができるそうです。

BASE+(INDEX*SCALE)+DISPLACEMENT
  • DISPLACEMENT: アドレス、もしくはベースレジスタからのオフセットを示す。定数。
  • BASE: ベースレジスタ
  • INDEX: インデックスレジスタ、配列の添え字のようなもの
  • SCALE: 2,4,8 のいずれか。配列要素のサイズを指定する。

そして、これを次のように記述するらしいです。

DISPLACEMENT(BASE, INDEX, SCALE)

注意点

ここがポイントなのですが、

アドレッシングモードを利用するときには必ず型を守らなくてはいけません。

特に、 BASEをレジスタにするのを忘れて エラーになるケースが多いような気がします。

コンパイラはjunk errorとか適当なエラーメッセージしか出してくれないので、 何がいけないのか分からずに苦労しました。

  • DISPLACEMENTには定数を指定する。
  • BASEとINDEXにはレジスタを指定する。
  • DISPLACEMENTはバイト単位(long型の配列なら、添字*4を指定する)。

これらを踏まえると、%eaxにインデックス、%ecxに配列arrayの先頭アドレスを格納したとき、 array[%eax+2]を参照するためには、次のようにする必要がありました。

8(%ecx, %eax, 4) # array[%eax+2]

慣れている人にとっては常識かもしれませんが、 普段RubyとかC#しか書いていない人にとっては苦行でした。

また、%ecxに配列aの先頭アドレスを格納するときには、 mov $a, %ecxというようにaにダラーが必要なのですが、これも知らないと間違えそうです。

成果物

最終的にfib(10)を求めてebxに格納するx86のアセンブラのコードはこんなのになりました。

アセンブラは難しいです。

# fib-x84asm.s
.data
.align 4
a:
  .long 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0

.text
.global main
main:
  mov $0, %eax # ループカウンタ
  mov $a, %ecx # aの先頭ポインタ

L1:                          # for(eax=0; eax<9; eax++)
  cmp $9, %eax
  je L2

  mov (%ecx,%eax, 4), %ebx   # ebx = a[eax]
  add 4(%ecx, %eax, 4), %ebx # ebx += a[eax+1]
  mov %ebx, 8(%ecx, %eax, 4) # a[eax+2] = ebx

  add $1, %eax
  jmp L1

L2:
  call stop
comments powered by Disqus

gam0022.net's Tag Cloud