UP | HOME

アセンブリコードでの配列参照について - 2023年度 システムプログラミング

アセンブリコードでの配列参照について

はじめに

乱暴にいうと,配列とは,メモリの塊を確保して,その先頭に名前を付けたものです. C言語における配列は,アセンブリコードではどのように表現され,アクセスされるのでしょうか.

領域の確保

メモリ中に n バイトの領域をあらかじめ空けておくアセンブラ指令を学習しました.例えば,

array:
  .space 400

としておくことで,400バイト分のデータ (100個の int の配列) の先頭を指すアドレスを array とすることができます. つまり,C言語でいうところの,

static int array[100];

の宣言にあたります.int が 32ビットで,かつ static (静的にはじめから領域が確保されている)配列宣言のことです.

配列の名前 = ラベル

array: というラベルは,配列として確保したメモリの先頭アドレスを表していますから, 仮に 8000 番地だとすると,8000 という数を表していることになります. 従って,この例で array[0] の値を読み書きするには, 8000番地から4バイト(int は 4バイトですね)の値を取り出してやればいいことになります.

アセンブリコードで書くとこうです:

la $a0, 8000       # $a0 に 8000を代入する
lw $a1, 0($a0)     # $a0 のアドレスから 4バイト(word)
                   # 取り出して $a1 に代入(load)

li ではなく la を使う理由は,FAQ を参照してください.8000はラベルで書いてもいいので,

la $a0, array      # $a0 に array (=8000)を代入する
lw $a1, 0($a0)     # $a0 のアドレスから 4バイト(word)
                   # 取り出して $a1 に代入(load)

となります. $a1 = array[0] を実現したことになります.

しかし,このままでは,0番目の要素しか取り出せないので,芸がありません. そこで,次は, $t0 番目の要素を取り出せるようにしましょう. $t0 番目の要素は, array から $t0 * 4 バイト目に入っているはずですから, アセンブリコードとCを混ぜ込ぜで書くと,

$a0 = array + $t0 * 4
lw $a1, 0($a0)

こうなりそうです.もう少しアセンブリコードに展開すると,

1: la $a0, array
2: $a0 = $a0 + $t0 * 4
3: lw $a1, 0($a0)

となるはずです.あとは,2行目をアセンブリコードになおせば完成です.

la   $a0, array
addu $t0, $t0, $t0  # $t0 を2倍に
addu $t0, $t0, $t0  # $t0 を更に2倍に
addu $a0, $a0, $t0  # $a0 = $a0 + $t0
lw   $a1, 0($a0)

ここで, $a1 = array[$t0] を実現したことになります. $a1 の値に1加えて,再び同じメモリに書込めば, array[$t0]++ を実現したことになりますね.

addu $a1, $a1, 1
sw   $a1, 0($a0)    # $a0 のアドレスに 4バイト(word)
                    # を書込む(store)

つまり,通して書くと, array[$t0]++ は,

la   $a0, array     # $a0 を array の先頭アドレス(= 8000)にする
addu $t0, $t0, $t0  # $t0 を倍に
addu $t0, $t0, $t0  # $t0 を更に倍に  ($t0 = $t0 * 4)
addu $a0, $a0, $t0  # $a0 = $a0 + $t0 ($a0 = 8000 + $t0 * 4)
lw $a1, 0($a0)      # $a0 のアドレスから 4バイト(word)
                    # 取り出して $a1 に代入(load)
addu $a1, $a1, 1    # $a1++
sw   $a1, 0($a0)    # $a0 のアドレス(array[$t0])
                    # に 4バイト(word)を書戻す(store)

として実現できます.更に高度な例として, primes[match++] = n を考えてみましょう. 同じ手順を踏んでいくだけですから,難しくはありません. match$s0, n$s1 として,分解すると,

primes[match++] = n

は,

primes[$s0] = $s1
$s0++

と書けます.更に,これまでと同じように,

la      $a1, primes    # $a1 = primes
move    $a0, $s0       # $s0 を $a0 に代入
                       # ($s0 つまり match が式中で壊れないようにするため)
addu    $a0, $a0, $a0  # $a0 (match) を倍に
addu    $a0, $a0, $a0  # $a0 を更に倍に ($a0 = match * 4)
addu    $a0, $a1, $a0  # $a0 = $a1 + $a0
                       #     = primes + match * 4
sw      $s1, 0($a0)    # $a0 のアドレス (primes[match]) に
                       # n ($s1) を書く(store)
addu    $s0,$s0,1      # match++

と書けることがわかるでしょう.

Author: Yoshinari Nomura

Emacs 27.1 (Org mode 9.3)