#概要
MIPSについてまとめてみた
MIPSのプログラムの実行はシミュレータのSPIMを使用している
#Hello World
##Hello Worldの表示
最初にHello Worldを表示するプログラムを作ってみる。
#Hello Worldを表示するプログラム
#データ
.data
msg: .asciiz "Hello World\n"
#テキスト
.text
.globl main
main:
li $v0, 4
la $a0, msg
syscall
jr $ra
# end
Hello World
##hello.asmの説明
#
の後はコメントアウト
プログラムはデータ領域とテキスト領域で構成される。
###データ領域
データ領域とは変数値や計算の途中で一時的に使われる領域のこと。
.data
msg: .asciiz "Hello World\n"
1行目の.data
はデータ領域の開始の宣言を表す。
2行目のmsg: .asciiz "Hello World\n"
はラベルmsg
の宣言をしている。
ラベルを宣言することによって、このアドレスをmsg
という記号で参照することができる。
###テキスト領域
テキスト領域とはプログラムを配置する領域である。
.text
.globl main
main:
li $v0, 4
la $a0, msg
syscall
jr $ra
# end
1行目の.text
でテキスト領域の開始の宣言をする。
2行目の.globl main
で下で定義されているmain
というラベルがグローバル(このプログラムの外部から見える)であることを宣言する。
3行目でラベルmain
を宣言。
4行目li $v0, 4
は$v0レジスタに値4をロードする。
5行目la $a0, msg
$a0はレジスタに付加パラメータ(この場合は表示する文字列を格納した領域のアドレス)をロードする。
6行目syscall
はシステムコールを実行する。実行するシステムコールの種類は$v0で指す
7行目jr $ra
は呼び出しもとに戻る(return)。アドレスは$raに格納されている。
#レジスタ
MIPSのレジスタは32個ある。
$s0〜$7 (16〜23)
プログラムの変数の値を保持
$t0〜$t7 (8〜15)
計算途中の一時的な値を保持
$zero (0)
常に0という値を持つ特別なレジスタ
#アセンブリ命令
##算術演算
###add
add $s1, $s2, $s3
$s1 = $s2 + $s3
###sub
sub $s1, $s2, $s3
$s1 = $s2 - $s3
###addi
addi $s1, $s2, num
$s1 = $s2 + num
##データ転送
load命令とstore命令がある。
- load命令 : 主記憶かrレジスタに値を転送する
- store命令 : レジスタから主記憶に値を転送する
###lw
lw $s1, num($s2)
$s1 = メモリ[$s2 + num]
###sw
sw $s1, num($s2)
$s1 = メモリ[$s2 + num]
##論理演算
###and
and $s1, $s2, $s3
$s1 = $s2 \quad \& \quad $s3
###or
nor $s1, $s2, $s3
$s1 = $s2 \quad || \quad $s3
###nor
nor $s1, $s2, $s3
$s1 = ! ($s2 \quad || \quad $s3)
###sll
sll $s1, $s2, 10
$s1 = $s2 \quad << \quad 10
###sli
sli $s1, $s2, 10
$s1 = $s2 \quad >> \quad 10
##無条件ジャンプ
###j
j 2500 意味 : go to 2500
#MIPSのフィールド
op | rs | rt | rd | shamt | funct |
---|---|---|---|---|---|
6bit | 5bit | 5bit | 5bit | 5bit | 6bit |
- OP : 命令の基本操作。オペコード
- rs : 第1のソース・オペランドのレジスタ
- rt : 第2のソース・オペランドのレジスタ
- rd : ディスティネーション・オペランドのレジスタ。結果を収める先
- shamt : シフト量
- funct : 機能コード。命令操作フィールドのバリエーションを表す。
#実際のプログラム例
##assing.asm
int src = 1234;
int dst;
int main()
{
dst = src;
return 0;
}
これをMIPSのコードにコンパイルすると
# データ領域の宣言
.data
# int src = 1234
.globl src
src: .word 1234
# int dst
.globl dst
dst: .word 0
#データ領域の宣言の終了
.text
# int main ()
.globl main
main:
# dst = src;
la $t0, src # srcのアドレス値をt0にロードする
lw $s0, 0($t0) # t0の内容をs0にロードする
la $t1, dst # dstのアドレス値をt1にロードする
sw $s0, 0($t1) # s0の内容をt1にストアする
#return 0
li $v0, 0 # $v0に値9をロードする
jr $ra # 呼び出しもとに戻る
##addition.asm
int x1 = 1234;
int x2 = 5678;
int y;
int main()
{
y = x1 + x2;
return 0;
}
これをMIPSのコードにコンパイルすると
# データ領域開始の宣言
.data
# int x1 = 1234
.globl x1
x1: .word 1234
# int x2 = 5678
.globl x2
x2: .word 5678
# int y
.globl y
y: .word 0
#テキスト領域の開始
.text
# int main
.globl main
main:
# y = x1 + x2
la $t0, x1
lw $s0, 0($t0)
la $t0, x2
lw $s1, 0($t0)
add $s0, $s0, $s1
la $t0, y
sw $s0, 0($t0)
# return 0
li $v0, 0
jr $ra
##addition2.asm
addition2.c
はaddition.c
とほぼ変わらないが、初期値宣言をしていない。
int p;
int q;
int r;
int main()
{
p = 1;
q = 2;
r = p + q;
return 0;
}
これをコンパイルすると
# データ領域の開始の宣言
.data
# int p
.globl p
p: .word 0
# int q
.globl q
q: .word 0
# int r
.globl r
r: .word 0
# テキスト領域の開始の宣言
.text
# int main
.globl main
main:
# p = 1
la $t0, p
li $s0, 1
sw $s0, 0($t0)
# q = 2
la $t0, q
li $s0, 2
sw $s0, 0($t0)
# r = p + q
la $t0, p
lw $s0, 0($t0)
la $t0, q
lw $s1, 0($t0)
add $s0, $s0, $s1
la $t0, r
sw $s0, 0($t0)
# return 0
li $v0, 0
jr $ra
addition2.asm
とaddition.asm
を比較してみると初期値宣言をしているaddition.asm
の方が効率が良いことが分かる。
#関連記事