(MIPS) 初学者向け SIPMで領域を扱う
内容
データセグメントにてメモリ(領域)を確保する際に、その扱い方をします。 (つまり、グローバル変数でメモリ(領域)を定義します。)
メモリに対して使う主な命令(32bit機とする,64bit機なら1Word=8byte)
命令 | 使用例 | 意味 |
---|---|---|
la | la $rt,即値($rs) | $rtに$rsのアドレスを入れる |
lw | lw $rt,即値($rs) | $rtに$rsの1Word(4byte)分を入れる |
sw | sw $rt,即値($rs) | $rsから1Word(4byte)分を$rtを入れる |
lb | lb $rt,即値($rs) | $rtに$rsの1byte分を入れる |
sb | sb $rt,即値($rs) | $rsから1byte分を$rtを入れる |
補足
laは$rt=&rsと同等である。
char*s[10]ならchar型は1byteであるから、lb,sbを多用する
int*s[10]ならint型は4byteであるから、lw,swを多用する
こんな感じである
intにおけるメモリの使い方。
取得、変更、表示を行う。
まずはc言語で記述
int s[4]; void print(int n) { //print関数 int i = 0; for (i = 0; i < 4; i++) { printf("%d %d\n", &s[i], s[i]); //アドレスと値を表示 } } void main() { s[0] = 0; s[1] = 0; s[2] = 0; s[3] = 0; print(4); //全て0 s[0] = 1; s[1] = 2; s[2] = 3; s[3] = 4; print(4); //1,2,3,4を順に表示 }
結果(c言語)
#アドレス #値 862081056 0 862081060 0 #アドレスが4ずつ増えている 862081064 0 862081068 0 862081056 1 #アドレスが一番上と同じ 862081060 2 862081064 3 862081068 4
.data s: .space 16 #領域確保 sp: .asciiz " " #スペース 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 sw $zero, 4($a0) # $a0 <- 0 sw $zero, 8($a0) # $a0 <- 0 sw $zero, 12($a0) # $a0 <- 0 li $a1,0 #ループ用のindex li $a2,16 #ループ用の限界値(ループ回数) jal print #print la $a0,s # $a0 <- sのポインタ li $t0,1 # $t0 = 1 sw $t0, 0($a0) # $a0 <- 1 addi $t0, $t0, 1 addi $a0, $a0, 4 # $a0++4 sw $t0, 0($a0) # $a0 <- 2 addi $t0, $t0, 1 addi $a0, $a0, 4 # $a0++ 4 sw $t0, 0($a0) # $a0 <- 3 addi $t0, $t0, 1 addi $a0, $a0, 4 # $a0++ 4 sw $t0, 0($a0) # $a0 <- 4 li $a1,0 #ループ用のindex li $a2,16 #ループ用の限界値(ループ回数) jal print #print lw $ra, 0($sp) #戻す add $sp, $sp, 4 #退避場所からの移動 jr $ra print: la $a0,s # $t0 <- sの領域アドレス add $a0,$a0,$a1 # sのアドレス+α move $t0, $a0 # $t0に$a0の保存 la $a0,0($a0) # $a0 <- $t0のアドレス li $v0, 1 # syscall用 syscall # 整数値($a0)の表示 la $a0, sp # スペース用 li $v0, 4 # syscall用 syscall # スペースの表示 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
結果(アセンブリ言語)
#アドレス #値 268500992 0 268500996 0 #アドレスが4ずつ増えている 268501000 0 268501004 0 268500992 1 #アドレスが一番上と同じ 268500996 2 268501000 3 268501004 4
注意点
- laとlwをしっかりと使い分ける
- 4byte分をしっかりと加算する
- アドレスがc言語、アセンブリ言語共に4ずつ増えている。
- lwやswの即値の部分にレジスタの値(変数)は使えないので、即値($rs)の$rsの部分をループごとに加算していくようにする
- $a0は一つしかなく、syscallで毎回使うので、アドレスの位置をずらすときは$a0に直接加算するのではなく、レジスタを使う。もし直接$a0に加算するなら、改行等のsyscallの時に退避させる。
charにおけるメモリの使い方。
取得、変更、表示を行う。
まずはc言語で記述
#include <stdio.h> char s[] = "abcd"; void print(int n) { // print関数 int i = 0; for (i = 0; i < 4; i++) { printf("%d %s\n", &s[i], (s + i)); //アドレスと値を表示 } } void main() { print(4); // abcdと表示 s[0] = 'A'; s[1] = 'B'; s[2] = 'C'; s[3] = 'D'; print(4); // ABCDと表示 }
結果(c言語)
#アドレス #値 1104089104 abcd 1104089105 bcd #アドレスが1ずつ増えている 1104089106 cd 1104089107 d 1104089104 ABCD #アドレスが一番上と同じ 1104089105 BCD 1104089106 CD 1104089107 D
.data before: .asciiz "abcde" .asciiz "A" .asciiz "B" .asciiz "C" .asciiz "D" sp: .asciiz " " #スペース nl: .asciiz "\n" #改行 .text .globl main main: add $sp, $sp, -4 # 退避場所の確保 sw $ra, 0($sp) # 退避 la $a0, before # $a0 <- beforeのポインタ li $a1,0 #ループ用のindex li $a2,4 #ループ用の限界値(ループ回数) jal print #print la $a0,before # $a0 <- beforeのポインタ lb $t0,after_1 # $a0 <- after_1のポインタ sw $t0, 0($a0) # $a0 <- "A" addi $t0, $t0, 1 # "A"の次の"B"を指す addi $a0, $a0, 1 # $a0++1 sb $t0, 0($a0) # $a0 <- "B" addi $t0, $t0, 1 # "B"の次の"C"を指す addi $a0, $a0, 1 # $a0++ 1 sb $t0, 0($a0) # $a0 <- "C" addi $t0, $t0, 1 # "C"の次の"D"を指す addi $a0, $a0, 1 # $a0++ 1 sb $t0, 0($a0) # $a0 <- "D" li $a1,0 #ループ用のindex li $a2,4 #ループ用の限界値(ループ回数) jal print #print lw $ra, 0($sp) #戻す add $sp, $sp, 4 #退避場所からの移動 jr $ra print: la $a0,before # $t0 <- beforeの領域アドレス add $a0,$a0,$a1 # beforeのアドレス+α move $t0, $a0 # $t0に$a0の保存 la $a0,0($a0) # $a0 <- $t0のアドレス li $v0, 1 # syscall用 syscall # 整数値($a0)の表示 la $a0, sp # スペース用 li $v0, 4 # syscall用 syscall # スペースの表示 move $a0,$t0 # $a0 <- $t0のアドレスの値 syscall # 整数値($a0)の表示 la $a0, nl # 改行用 syscall # 改行の表示 add $a1,$a1,1 # ループ用のインデックス bne $a1,$a2 print # ループ jr $ra
結果(アセンブリ言語)
#アドレス #値 268500992 abcd 268500993 bcd #アドレスが4ずつ増えている 268500994 cd 268500995 d 268500992 ABCD アドレスが一番上同じ 268500993 BCD 268500994 CD 268500995 D
注意点
- laとlbをしっかりと使い分ける
- 1byte分をしっかりと加算する
- アドレスがc言語、アセンブリ言語共に1ずつ増えている。
- データセグメントはつながっている。(名前を指定しないでafter_1のみをロードして、後はそのアドレスに1を加算していたがうまくいっている)
まとめ
- la,lw,sw,sb,lbをしっかりと使い分ける必要がある。
- intは4byte,charは1byteずつ移動していることに注目する