bne $t0, $zero, L # if($t0!=0) 레이블 L로 이동; -> 즉, 레이블 L은 s1 <s2일 때 실행하는 레이블
<,>= 명령어 연산이 =,!=보다 느리다.
숫자 비교 명령어
slt $t0, $s0, $s1 #부호 있는 이진수 비교 -> $s0 <$s1이면 $t0=1, $s0>$s1이면 $t0=0
slti는 레지스터가 아닌 상수와 바로 비교
sltu $t0, $s0, $s1 #부호 없는 이진수 비교
sltui는 상수와 비교
slt일떈 $s0이 -1로 인식되고 sltu일땐 40억으로 인식됨
2.8 함수 콜
<필요한 레지스터>
$a0 ~ $a3 : arguments 저장할 레지스터
$v0, $v1 : 결과 값 저장할 레지스터
$t0 ~ $t9 : 임시로 값 저장할 레지스터
$s0 ~$s7 : 값을 저장할 레지스터
값을 저장하고 원래 함수로 돌아가기 전에 처음 값으로 반드시 복구해줘야 한다.
$sp : stack pointer
$fp : frame pointer
$ra : return address
<필요한 명령어>
함수 콜 명령어 jal procedureLabel : $ra에 다음에 수행해야 할 명령어의 주소를 넣어준 뒤 그 주소로 점프
함수 리턴 명령어 jr $ra : 프로그램 카운터에 리턴 주소 복사
예 1)
int leaf_example (int g, h, i, j)
{ int f = (g + h) -(i+j);
return f;
}
/* Arguments g, …, j in $a0, …, $a3
f in $s0 (hence, need to save $s0 on stack)
Result in $v0 */
# 스택 공간 확보
addi $sp, $sp, -4
# 원래 f값 저장
sw $s0, 0($sp)
#함수 본문 실행
add $t0, $a0, $a1
add $t1, $a2, $a3
sub $s0, $t0, $t1
#결과 값 저장
add $v0, $zero, $s0
#원래 f값 복구
lw $s0, 0($sp)
#스택 복구
addi $sp, $sp, 4
# 다음에 실행할 명령어로 리턴
jr $ra
예 2)
int fact (int n)
{
if (n < 1) return 1;
else return n * fact(n-1);
}
/* Argument n in $a0
Result in $v0 */
# 스택 공간 확보
addi $sp, $sp, -8
# argument 레지스터 값 복원위해 원래 값 보관
sw $a0, 0($sp)
# 재귀함수라 리턴 주소를 복원하기 위해 저장
# -> 함수 호출이 끝나고 돌아갈 주소를 미리 저장해놓지 않으면 함수가 끝나지 않고
# 계속 호출되는 무한루프에 빠질 수 있다.
sw $ra, 4($sp)
#본 함수 실행
slti $t0, $a0, 1 # n < 1 이면 $t0=1, 아니면 $t0=0
beq $t0, $zero, L1 # $t0=0이면 L1 실행. 즉, n > 1인경우
# n < 1 일때
addi $v0, $zero, 1 #결과값 1 저장
addi $sp, $sp, 8 # 스택 복원
jr $ra #함수 리턴
L1: addi $a0, $a0, -1
jal fact # fact 함수(자기 자신) 호출
# 원래값들 복원
lw $a0, 0($sp)
lw $ra, 4($sp)
# 스택 복원
addi $sp, $sp, 8
# 결과값 계산 후 리턴
mul $v0, $a0, $v0
jr $ra
2.9 byte/harlword(2바이트) 조작 명령어
lb rt, offset(rs) lhrt, offset(rs) -> rs에서 offset만큼 떨어진 주소에 저장된 값을 rt에 저장 # b는 바이트, h는 2바이트 이동. 남은 비트는 sign extension
lbu rt, offset(rs) lhu rt, offset(rs) -> rs에서 offset만큼 떨어진 주소에 rt를 저장 # unsigned 버전. 남은 바이트 0으로 채우기(zero extension)
sb rt, offset(rs) sh rt, offset(rs) -> rs에서 offset만큼 떨어진 주소에 rt값을 저장
예) char은 1바이트이고 i는 양수이므로 lbu 사용
void strcpy (char x[], char y[])
{ int i ;
i=0;
while ((x[i]=y[i])!=' 0')
i += 1;
}
/* Addresses of x, y in $a0, $a1
i in $s0 */
addi $sp, $sp, -4 #스택 공간 확보
sw $s0, 0($sp) #원래 i값 스택 포인터 레지스터에 저장
#본 함수
L1: add $s0, $zero, $zero # i = 0
add $t1, $s0, $a1 # $t1에 y[i] 주소 저장
lbu $t2, 0($t1) # $t2에 y[i] 값 저장
add $t3, $s0, $a0 # $t3에 x[i] 주소 저장
sb $t2, 0($t3) # x[i]=y[i]
beq $t2, $zero, L2 # y[i]값이 0과 같다면 L2레이블로 이동
addi $s0, $s0, 1 # 같지않다면 i=i+1
j L1 # L1레이블로 점프(loop)
# 함수 끝낼 준비
L2: lw $s0, 0($sp) # 원래 i값 복구
addi $sp, $sp, 4 # 스택 공간 복구
jr $ra # 함수 return
2.10 상수 작업 및 메모리 주소에 32비트 값을 사용하는 방법
32비트 상수
대부분의 상수는 16비트로 커버가능
16비트로 커버 불가능한 상수를 위한 명령어 -> lui rt, constant : 16비트의 상수를 rt레지스터의 왼쪽 16비트에 복사 후 오른쪽 16비트는 0으로 클리어
Branch Addressing
대부분의 타깃 브랜치는 가까운 곳에 있다
만약 타깃 브랜치가 멀리 있다면 어셈블러가 코드를 재작성한다.
예) 만약 L1 레이블(타깃 브랜치)이 멀리 있다면
beq $s0,$s1, L1
↓
bne $s0,$s1, L2
j L1
L2: ....
타깃 주소= PC(프로그램 카운터: 다음에 실행할 명령어 주소 저장되어 있다) + offset*4 -> pc는 다음 명령을 수행하기 위해 항상 offset을 1 증가시킨다. 따라서 원하는 offset 보다 1 낮게 써야 함.
Jump Addressing
타깃 주소 = address*4
예)
2*4=8만큼 가면 80020이지만 pc로 인해 1 offset 갔기 때문에 Exit 레이블 주소는 80024.