状況
アセンブリ言語をやっていて、領域に対しての操作が複数あり、特にループ文の中でポインタを操る方法をまとめました。
言葉で説明すると、データセグメントで、領域を確保し、その後、テキストセグメントで命令を実行します。
.data s: .space 20 #領域確保 nl: .asciiz "\n" #改行 .text .globl main main: add $sp, $sp, -4 # 退避場所の確保 sw $ra, 0($sp) # 退避 la $a0, s # $a0 <- sのポインタ sw $zero, 0($a0) # $a0 <- 0 addi $a0, $a0, 4 # $a0<- $a0 + 4 sw $zero, 0($a0) # $a0 <- 0 li $a1,0 #5回printを繰り返す li $a2,20 #5回printを繰り返す jal print #print lw $ra, 0($sp) #戻す add $sp, $sp, 4 #退避場所からの移動 jr $ra print: la $t0,s # $t0 <- sの領域アドレス add $t0,$t0,$a1 # sのアドレス+α lw $a0,0($t0) # $a0 <- $t0のアドレスの値 li $v0, 1 # syscall 用 syscall # 整数値($a0)の表示 la $a0, nl # 改行用 li $v0, 4 # syscall 用 syscall # 改行の表示 add $a1,$a1,4 # ループ用のインデックス bne $a1,$a2 print # ループ jr $ra
このとき、領域のポインタを変更する方法としていくつかの方法が挙げられる。(領域がint型(4バイト)であるとする。)
- 毎回pの指す場所を変える。
- 1の改良版(即値の利用)
- *(p+i)を使い、代入時に位置を指定する
1 毎回pの指す場所を変える。
実行前にp=p+1のようなコードを書くことにより実現する。
int型は4バイトであるので、(p+1)はp+4を意味する必要がある。
int *a=&s; //aに領域のアドレスを入れる *a=0; //領域の0番目 <- 0 a=a+1; //(1は自動的に4として扱われる) *a=0; //領域の4番目 <- 0
la $a0, s # $a0 <- sのポインタ sw $zero ,0($a0) #領域の0番目 <- 0 addi $a0,$a0,4 #領域の先頭から4番目を指す sw $zero ,0($a0) #領域の4番目 <- 0
2. 1からaddiを削除する(即値の利用)
int *a=&s; //aに領域のアドレスを入れる *a=0; //領域の0番目 <- 0 *(a+1)=0; //!!変更点!!
la $a0, s # $a0 <- sのポインタ sw $zero ,0($a0) #領域の0番目 <- 0 sw $zero ,4($a0) #領域の4番目 <- 0
このやり方は、値を入れる回数が整数値(即値)でわかるときのみ有効であり、関数を作成したときの実用性的には若干低めである。
3. *(p+i)を使い、代入時に位置を指定する
その前に一度変数を使ったやり方
int *a=&s; //aに領域のアドレスを入れる i=0; //初期化 *a=0; //領域の0番目 <- 0 i=i+4; //i=4 a=a+i; //領域の先頭から4番目を指す *a=0; //領域の4番目 <- 0 a=a-i; //領域の先頭から0番目を指す i=i+4; //i=8 a=a+i; //領域の先頭から8番目を指す *a=0; //領域の8番目 <- 0 a=a-i; //領域の先頭から0番目を指す
la $a0, s # $a0 <- 領域のポインタ li $t0,0 # $t0 = 0 (= i) sw $zero ,0($a0)# 領域の0番目 <- 0 addi $t0,$t0,4 # $t0 = 4 add $a0,$a0,$t0 # 領域の先頭から4番目を指す sw $zero ,0($a0)# 領域の4番目 <- 0 sub $a0,$a0,$t0 # 領域の先頭から0番目を指す
subしてaddしてと無駄が多いのではないかと思うかもしれないが、*(p+i)等のポインタに対して、変数を渡して、ポインタの指す位置を変更することを実現するためには必要なことである。 今回、毎回subする形にしているが、これにより、pの値をほかの場所で使う際も気にせずに使うことができる。(独立性の確保)
本題
int *a=&s;// aに領域のアドレスを入れる int i=0; // 初期化 *a=0; // 領域の0番目 <- 0 i=i+1; // *(a+i)と書いたときにiの値は強制的に4倍される *(a+i)=0; // 領域の4番目 <- 0 i=i+1; // i = 2 *(a+i)=0; // 領域の8番目 <- 0
同じコードを繰り返しているつまり、、
int *a=&s; //aに領域のアドレスを入れる int i=0; //初期化 *a=0; //領域の0番目 <- 0 for(i=0;;i++){ //条件式なし *(a+i)=0; //領域のi\*4番目 <- 0 }
ここでiの加算値を1にしておくことで条件式等を考えるときに楽である。
これをアセンブリ言語にすると以下のようになる。
la $a0, s # $a0 <- 領域のポインタ li $t0,0; # $t0 = 0 (= i) sw $zero ,0($a0)# 領域の0番目 <- 0 addi $t0,$t0,1 # $t0 = 1 mult $t0, 4 # $t0 * 4 mflo $t2 # $t2 = 4 add $a0,$a0,$t2 # 領域の先頭から4番目を指す sw $zero ,0($a0)# 領域の4番目 <- 0 sub $a0,$a0,$t2 # 領域の先頭から0番目を指す addi $t0,$t0,1 # $t0 = 2 mult $t0, 4 # $t0 * 4 mflo $t2 # $t2 = 8 add $a0,$a0,$t2 # 領域の先頭から8番目を指す sw $zero ,0($a0)# 領域の8番目 <- 0 sub $a0,$a0,$t2 # 領域の先頭から0番目を指す
indexの値を常に4ずつ上げる
la $a0, p # $a0 <- 領域のポインタ li $t0,0 # $t0 = 0 (= i) sw $zero ,0($a0)# 領域の0番目 <- 0 addi $t0,$t0,4 # $t0 = 4 add $a0,$a0,$t0 # 領域の先頭から4番目を指す sw $zero ,0($a0)# 領域の4番目 <- 0 sub $a0,$a0,$t0 # 領域の先頭から0番目を指す addi $t0,$t0,4 # $t0 = 8 add $a0,$a0,$t0 # 領域の先頭から8番目を指す sw $zero ,0($a0)# 領域の8番目 <- 0 sub $a0,$a0,$t0 # 領域の先頭から0番目を指す
まとめ
- 領域に対する操作で様々な方法で領域に文字を代入した。
- 即値を使うやり方は、領域に入れたい値(定数で)と位置が確定しているのであればとても有効である。
- multを用いたやり方が汎用性は高いと思われる。