20260601_2


繰り返し

ループは条件分岐にジャンプ文を足すことで実現できる

break;はジャンプすればOK

広域配列

広域に配列を確保するため配列そのものはメモリ上に配置

int sum =0, a[]={-1,2,3};
sum += a[0];
sum += a[1];
sum += a[2];

	.data
	.align #aは4バイト境界とする
a:  .word    -1,2,3
	.text
	.global  main
main:
	move $t0,$zero
	la   $t1,a
	lw   $t2,0($t1)
	add  $t0,$t0,$t2
	lw   $t2,4($t1)
	add  $t0,$t0,$t2
	lw   $t2,8($t1)
	add  $t0,$t0,$t2
	
	li   $v0,1
	move $a0,$t0
	syscall
	jr   $ra

aの先頭アドレスを$t1に入れ、<定数の変位>($t1)で要素にアクセスする

MIPSの一般的なメモリ配置

ハードウェア側から強制されるわけではない

予備
テキストセグメントプログラム領域(くらい)
静的データ実行前に存在が判明しているデータセグメント
帯域変数や文字列など、.dataで宣言するもの
動的データmallocなどで実行時に動的に確保されるデータ
スタック関数呼び出しに関する情報や、仮引数を含む局所変数のための領域
カーネル用
int sum = 0,a[]={-1,2,3}
for(i=0;i<3;i++) sum += a[i];
main:
	li   $t0,0
	li   $t1,0 #変位
	la   $t2,a #基準アドレス
loop:
	bge  $t1,3,exit
	sll  $t3,$t1,2 #2ビット左シフト
	add  $t3,$t2,$t3 #t3に実行アドレスを格納
	lw   $t3,0($t3)
	add  $t0,$t0,$t3
	addi $t1,t1,1
	j loop

基準アドレスを変化させて命令を削減する

main:
	li $t0, 0 # sum=0
	li $t1, 0 # i=0
	la $t2, a # $t2=a
loop:
    bge $t1, 3, exit # if i>=3 then goto exit
    lw $t3, 0($t2) # $t3=*($t2)
    add $t0, $t0, $t3 # sum+=$t3
    addi $t1, $t1, 1 # i++ (終了判定専用)
    addi $t2, $t2, 4 # $t2+=4 (次の要素のアドレスへ)
    j loop

終了の判定を変更して命令を削減する

main:
    li $t0, 0 # sum=0
    la $t2, a # p=a (aの先頭を指す)
    add $t1, $t2, 12 # q=p+3 (aの末尾の次を指す)
loop:
    bge $t2, $t1, exit # if p >= q then goto exit
    lw $t3, 0($t2) # $t3 = *p
    add $t0, $t0, $t3 # sum += $t3
    addi $t2, $t2, 4 # p++
    j loop

文字列をコピーする

	  .data
from: .asciiz "abc"
to:   .space 4
      .text
      .global main

配列操作として扱う

int i;
char from[]="abc",to[4];
for(i=0;from[i]!='\0';i++){
	to[i]=from[i];
}
to[i]='\0';
main:
	li $t0, 0 # i=0
	la $t1, from # $t1=from
	la $t2, to # $t2=to
loop:
	add $t3

Cではポインタがさすデータのサイズをコンパイラが知っているが、アセンブリの場合はプログラマがこれを管理する


MIPSのサブルーチン

jal命令によって呼び出し

このときは$raに対するジャンプがリターンに相当する

このとき$raを安全に扱うためにスタックに退避する

$raはcollee-saveレジスタ(使う前に退避して、使い終わったら元の値に復元する)

対比場所にはスタックを使うと都合がいい(FILOの都合)

MIPSではスタックを扱うとき

  • メモリ領域はスタックセグメントを
  • スタックポインタとして$spを使用する
  • $spではスタックトップ=そのデータの格納アドレスを保持
  • push/popはaddとload/storeによって実現する

サブルーチンを実現したことで引数や局所変数を用意できるようになれば関数を用意できる

広域変数は無限のエクステントを持つ

普通の変数はリターンとともに消滅する

MIPSによる関数定義の雛形

func:
add $sp, $sp, -(レジスタの退避領域+局所変数の領域) #メモリ領域を予約する
sw  退避するレジスタ,d1($sp)
...
sw  退避するレジスタ, dx($sp)
 
###関数本体、返り値は$v0へ###
 
lw レジスタ,d1($sp)
...
lw レジスタ,dx($sp)
add $sp,$sp,サイズ #$spを元に戻す
jr $ra

MIPSによる関数呼び出しの雛形

# $t,$a,$vを自身のフレームへ退避
# 第一引数から第四引数を$aに格納
# 第五引数以降はスタックへ
jal g
# addi $sp, $sp
# 退避した値を復元