본문 바로가기
소프트웨어/컴퓨터 구조(MIPS)

[MIPS] MIPS programming 과제 - factorial 연산

by Ruas 2021. 11. 16.
728x90

 

오늘은 MIPS를 사용하여 Factorial 연산을 수행하는 코드를 설명드리겠습니다.

 

해당 포스트를 진행하기에 앞서 기본적인 opcode 학습이 필요합니다.

 

 

[MIPS] MIPS programming 기본

 

[MIPS] MIPS programming 기본

opcode Artithmetic Instructions opcode 사용 방법 의미 add add $s1, $t0, $t1 $s1 = $t0 + $t1 addi addi $s1, $t0, Int $s1 = $t0 + Int sub sub $s1, $t0, $t1 $s1 = $t0 - $t1 mul mul $s1, $t0, $t1 $s1..

ruas-coding.tistory.com

 

 

Q. syscall을 사용하여 사용자에게 양의 정수 값 10을 입력 받고 1부터 입력 받은 양의 정수까지의 팩토리얼 값(10!)을 구한 후 그 값을 syscall로 출력하는 프로그램을 작성하시오.

 

이번 문제는 사용자의 입력을 받아 factroial 연산을 수행하는 프로그램입니다.

 

사용자의 입력을 저장할 변수와, 추후 factorial 연산의 for loop을 위한 변수를 선언합니다.

 

    .data
a: .word 0
c: .word 0
    .text

 

다음으로 main 함수에서 사용자 입력을 받고, 레지스터에 전달합니다.

 

main:
    li $v0, 5
    syscall
    sw $v0, a

    lw $t0, a

 

factroial 연산에서는 0인 경우, 음수인 경우, 1인 경우 등에 대한 예외처리가 필요하지만,

이번 문제에서는 양수일 경우만 예외처리를 하도록 하겠습니다.

 

때문에 1보다 큰 값이 입력되거나, 1이 입력되는 경우로 나누어 함수를 구성합니다.

 

먼저, 사용자가 입력한 값이 1인지 확인합니다.

만약 1이 입력된 경우,  number1 함수를 호출하는 구조로 코드를 구성합니다.

 

    beq $t0 , 1, number1

 

number1 함수에서는 별도의 연산이 필요하지 않습니다.

1의 팩토리얼은 1이기 때문에, 입력받은 값을 그대로 출력합니다.

 

number1:
    move $a0, $t0
    li $v0, 1
    syscall

    li $v0, 10
    syscall

 

만약 number1 함수로 진입한 경우라면,

출력이 완료된 이후에도 main 함수로 되돌아가지 않고

number1 함수에서 시스템을 종료시킵니다.

 

만약, number1 함수로 이동할 조건이 만족되지 않았다면(입력된 값이 1이 아닌경우)

위의 beq $t0 , 1, number1 를 무시하고 다음 명령어로 이동합니다.

 

beq 명령어를 통과했다는 것은 이제 정상적인 factorial 연산을 수행하면 된다는 의미입니다.

때문에, 연산에 필요한 for loop 변수를 레지스터로 전달합니다.

 

이렇게 되면 연산이 필요할 때 레지스터에 전달하는 형식으로 구현할 수 있습니다.

 

    lw $t1, c
    add $t1, $t0, $zero
    jal factorial

 

위의 코드를 한줄한줄 설명드리겠습니다.

 

먼저, 변수 c를 레지스터에 전달합니다. 현재 선언만 되있는 변수를 실제로 사용하기 위해 시스템으로 불러온다고 보시면 됩니다.

 

Factorial 연산을 하기 앞서, 반복문 구성이 필요합니다.

이 경우에는 사용자가 입력한 값에서 -1 의 값이 반복되어야 하므로, for loop을 사용합니다.

for loop를 위해서는 반복할 횟수를 저장하고 있는 별도의 변수가 필요합니다.

때문에 위에서 변수 c를 전달한 레지스터 $t1에 사용자의 입력 값을 전달하는 방식으로 진행합니다.

 

여기서 $zero는 0을 의미합니다. 때문에 add 명령어를 사용해서 덧셈을 하더라도,

$t0에 저장되어있는 값이 변질되지 않고 $t1으로 전달될 수 있습니다.

 

다른 전달방법도 존재하니 해당 방법을 사용해도 무관하겠습니다.

 

변수 전달이 모두 완료되면,

factorial 함수로 jump 합니다.

jal 명령어의 경우 본인의 다음 명령어의 주소를 레지스터에 저장하고,

해당 함수로 jump 하는 기능을 수행합니다.

때문에 함수에서의 연산이 완료되어도 다시 main 함수로 되돌아 올 수 있습니다.

 

factorial: 
    sub $t1, $t1, 1
    mul $t0, $t0, $t1

    bne $t1, 1, factorial

    jal $ra

 

Factorial 함수에서는 연산만 수행합니다.

위에서 선언한 변수 c의 값이 1이 될 때 까지 계속 반복하여 연산을 수행하며,

c가 1에 도달하면 main 함수에서 저장한 다음 명령어의 위치로 jump 합니다.

 

    move $a0, $t0
    li $v0, 1
    syscall

    li $v0, 10
    syscall

 

main 함수로 되돌아 오면

연산 결과를 출력하고 시스템을 종료합니다.

 

 

 

 

아래는 전체 코드 입니다.

 

    .data
a: .word 0 #입력 값
c: .word 0 #factorial 함수에서 for문 용도로 사용
    .text

main:
    li $v0, 5
    syscall
    sw $v0, a
    #변수 a 입력

    lw $t0, a #변수 a 전달

    beq $t0 , 1, number1 #1이 입력될 경우 1로 고정 출력하는 함수로 이동

    lw $t1, c #변수 c는 1이 아닌경우에만 필요하므로 number1 조건 확인 후 레지스터 전달
    add $t1, $t0, $zero #변수 a를 c에 복사
    jal factorial #factorial 함수로 이동

    move $a0, $t0
    li $v0, 1
    syscall
    #연산결과 출력

    li $v0, 10
    syscall
    #종료

factorial: 
    sub $t1, $t1, 1 #c의 값을 -1
    mul $t0, $t0, $t1 #마이너스한 c와 a를 곱하기

    bne $t1, 1, factorial c가 1이 될 때까지 factorial 함수 반복

    jal $ra #c가 1이 되면 다시 main 함수로 복귀

number1:
    move $a0, $t0
    li $v0, 1
    syscall
    #연산결과 출력

    li $v0, 10
    syscall
    #종료

 

이상입니다.

728x90

댓글