한참을 열심히 달려왔네요.
저만 달려 왔나요? 이번 글에서는 C 코드를 보여드리고
해당하는 부분을 코딩해 보며 마무리 하겠습니다.
void swap( int v [], int k )
{
int temp;
tmep = v[k];
v[k] = v[k+1];
v[k+1] = temp;
}
void sort ( int v[], int n)
{
int i, j;
for ( i =0 ; i<n ; i+=1{
for (j = i -1; j>=0 && v[j] > v[j+1]; j -=1) {
swap(v,j);
}
}
}
프로시저 swap의 MIPS 어셈블리 코드
swap : sll $t1, $a1, 2 # reg $t1 = k*4
add $t1, $a0, $t1 # reg $t1 = v + (k*4)
lw $t0, 0($t1) # reg $t0 (temp) = v[k]
lw $t2, 4($t1) # reg $t2 = v[k+1]
sw $t2, 0($t1) # v[k] = $t2
sw $t0, 4($t1) # v[k+1] = $t0
jr $ra # 호출 주소로 복귀
음 설명을 안하고 넘어가려다가, 적고 넘어갑니다. 우선 인수가 v, k 임으로 $a0, $a1 을 사용 합니다. 그리고 몸체에서 temp를 사용하므로 $t0 가 사용됩니다.
그럼 이제 v[k]를 찾는 과정을 봅시다. 바이트 주소를 사용함으로 k*4를 해줘야합니다. 해당 값을 $t1에 저장합시다.
sll $t1, $a1, 2 # reg $t1 = k*4
배열에서 이름은 주소를 나타냄으로 v[k]를 알기위해서는 v 주소에 k*4의 주소를 더해줘야 합니다.
add $t1, $a0, $t1 # reg $t1 = v + (k*4)
이로써 $t1은 v[k]의 address를 가지게 되었습니다.
이제 값을 lw 하겠습니다. v[k]의 값을 temp에 로드합니다. 0($t1)이 v[k]의 메모리 주소임을 기억하세요
lw $t0, 0($t1) # reg $t0 (temp) = v[k]
다음으로 $t2 임시 레지스터에 v[k+1]을 로드합니다. 물론 주소임으로 v[k]에 4를 더해줘야 겠죠.
lw $t2, 4($t1) # reg $t2 = v[k+1]
이제 값을 가져왔으니 바꿔 봅시다. v[k]에 $t2의 값을, v[k+1]에 $t0의 값을 넣어보죠
sw $t2, 0($t1) # v[k] = $t2
sw $t0, 4($t1) # v[k+1] = $t0
이로써 swap이 완료되었고, 이제 다시 호출 프로세서로 복귀하면 됩니다. ( jr $ra ) 간단하죠? 여러분 꼭 기억하셔야 할점은 메모리 주소를 가지고 이동하여 그 속의 값을 임시 변수에 로드하고 그 값을 필요한 주소에 스토어 한다라는 것만 기억하시면 이 예제는 매우 쉽게 이해하실 수 있습니다.
프로시저 sort의 MIPS 어셈블리 코드
sort: addi $sp, $sp,-20 # $s0- $s3 , $ra를 위한 스텍생성
sw $ra, 16($sp)
sw $s3, 12($sp)
sw $s2, 8($sp)
sw $s1, 4($sp)
sw $s0, 0($sp)
move $s2, $a0
move $s3, $a1
move $s0, $zero
for1tst: slt $t0, $s0, $s3
beq $t0, $zero, exit1
addi $s1, $s0, -1
for2tst: slti $t0, $s1, 0
bne $t0, $zero, exit2
sll $t1, $s1, 2
add $t2, $s2, $t1
lw $t3, 0($t2)
lw $t4, 4($t2)
slt $t0, $t4, $t3
beq $t0, $zero, exit2
move $a0, $s2
move $a1, $s1
jal swap
addi $s1, $s1, -1
j for2tst
exit2: addi $s0, $s0, 1
j for1tst
exit1: lw $s0, 0($sp)
lw $s1, 4($sp)
lw $s2, 8($sp)
lw $s3, 12($sp)
lw $ra, 16($sp)
addi $sp, $sp, 20
jr $ra
설명 시작하겠습니다. 우선 sort의 인수는 v와 n 임으로 $a0, $a1에 넣고 변수 i, j는 $s0, $s1을 할당합니다.
1. 첫 번째 ( 바깥쪽 ) 순환문 번역하기
for ( i = 0; i < n ; i += 1) 부분을 번역하겠습니다.
move $s0, $zero # i = 0
이 부분은 가장 마지막 부분에 해당합니다.
addi $s0, $s0, 1 # i += 1
조건 부분입니다. i <n 이 아니면 순환에서 빠져나옵니다. 즉 i >= n 이면 종료됩니다.
for1tst:slt $t0, $s0, $a1
beq $t0, $zero, $exit1
순환문의 맨끝은 처음의 반복조건 검사로 되돌아가는 명령인데요
j for1tst
exit1:
이 문장들을 정리하면 첫 번째 순환문은 다음과 같은 형태가 됩니다.
move $s0, $zero # i = 0
for1tst:slt $t0, $s0, $a1
beq $t0, $zero, $exit1
( 본문 내용 )
addi $s0, $s0, 1 # i += 1
j for1tst
exit1:
2. 두 번째 ( 안쪽 ) 순환문 번역하기
for ( j = i -1; j>=0 && v[j] > v[j+1]; j -= 1 ) 이 부분입니다.
초기화 부분은 addi 를 사용하면 간단히 해결됩니다.
addi $s1, $s0, -1 # j = i -1
j 값을 감소시키는 것 역시 addi를 사용합니다.
addi $s1, $s1, -1 # j -= 1
반복 조건 검사는 두 부분으로 구성됩니다. 둘중 하나라도 거짓 시 순환문에서 나갑니다.
for2tst:slti $t0, $s1, 0 # $s1 < 0 ( j < 0 ) 참이면 1 거짓이면 0
bne $t0, $zero, exit2
이 명령을 통과하였다면 두 번째 조건에 대해 검사합니다. 즉 v[j]<=v[j+1] 이면 순환문을 빠져나오면 됩니다. 먼저 jdp 4를 곱해서 v의 시작 주소에 더해야겠죠?
sll $t1, $s1, 2
add $t2, $a0, $t1
이 주소를 활용하여 v[j] 주소를 읽습니다.
lw $t3, 0($t2)
다음 원소의 주소는 4만큼 더 크므로, 레지스터 $t2에 4를 더해서 v[j+1]을 읽습니다.
lw $t2, 4($t2)
v[j] <= v[j+1] 의 검사는 v[j+1] >= v[j] 검사와 같으므로 다음 명령어로 수행할 수 있습니다.
slt $t0, $t4, $t3 # reg = 0 if $t4 >= $t3
beq $t0, $zero, exit2
마지막 명령어는 내부 순환문의 조건 검사로 점프하는 것이겠죠?
j for2tst
여기 까지 종합하면 2번 째 순환문은 아래와 같은 구성이 됩니다.
addi $s1, $s0, -1 # j = i -1
for2tst: slti $t0, $s1, 0 # $t0 = 1 if $s1 < 0 ( j < 0 )
bne $t0, $zero, exit2 # go to exit 2 if $s1 < 0
sll $t1, $s1, 2 # $t1 = j*4
add $t2, $a0, $t1 # $t2 = v + j*4
lw $t3, 0($t2) # $t3 = v[j]
lw $t4, 4($t2) # $t4 = v[j+1]
slt $t0, $t4, $t3 #$t0 = 0 if $t4 >= $t3
beq $t0 $zero, exit2
. . .
( body loop )
. . .
addi $s1, $s1, -1 # j-= 1
j for2tst # jump to test of inner loop
exit2:
잘 따라오셨나요?? 쉽지가 않아요. 그렇지만 이런 예제를 잘해놓고 나면 다음에 만났을 때, 쉽게 넘어갈 수 있게 되겠죠?
3. 프로시저 sort 호출
안 쪽 for문 본체 부분인 swap ( v, j ); 은 간단하게 바뀐답니다
jal swap
4. 인수 전달 sort
sort 프로시저도 레지스터 $a0, $a1을 사용하고, swap 프로시저도 인수 전달을 위해서 똑같은 레지스터를 사용해야 하므로 인수 전달 방법이 문제가 되는데요, 프로시저 앞부분 sort의 인수를 다른 레지스터에 복사해서 swap을 호출할 때 레지스터 $a0, $a1을 사용할수 있게 하는 겁니다. 물론 스택에 저장했다가 복구할 수 있지만 레지스터를 활용하면 더 빠르답니다.
move $s2, $a0 # copy parameter $a0 into $s2
move $s3, $a1 # copy parameter $a1 into $s3
다음은 swap에 인수를 전달하는 일입니다.
move $a0, $s2 #first swap parameter is v
move $a1, $s1 #second swap parameter is j
5. 프로시저 호출 전과 후의 레지스터 내용을 같게 유지하기
거의 다왔어요. 이제 남은 것은 레지스터 내용을 저장했다가 복구하는 프로그램이에요. sort 자신도 프로시저로서 호출된 입장이므로 레지스터 $ra의 복귀 주소를 저장해야 한답니다. 그 외에 저장해야하는 레지스터는 $s0-$s3가 있겠죠
addi $sp, $sp, -20
sw $ra, 16($sp)
sw $s3, 12($sp)
sw $s2, 8($sp)
sw $s1, 4($sp)
sw $s0, 0($sp)
와 고생하셨습니다. 다시 한번 위로 올라가서 전체 코드를 보시길 바랍니다. 어려운 내용은 없습니다. 다만 하나하나 빠트리지 않고 해셔야 한다는게 조금은 까다로울 뿐이지요. 이번 글은 여기까지 하겠습니다. 이상 WH 였습니다.
'컴퓨터 구조론( MIPS )' 카테고리의 다른 글
프로세서 2- Load or Store 명령어, branch 명령어 (0) | 2022.02.09 |
---|---|
프로세서 1 - R-type 명령어 데이터 패스, 명령어 인출과정 (0) | 2022.02.09 |
명령어 : 컴퓨터 언어 7 ( feat. 동기화, 번역, 실행) (0) | 2022.02.08 |
명령어 : 컴퓨터 언어 6 ( feat. 주소 지정 및 방식 ) (0) | 2022.02.08 |
명령어 : 컴퓨터 언어 5 (0) | 2022.02.07 |