-
Notifications
You must be signed in to change notification settings - Fork 6
/
factorial.s
133 lines (105 loc) · 4.73 KB
/
factorial.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
.globl main
.data
msgprompt: .word msgprompt_data
msgres1: .word msgres1_data
msgres2: .word msgres2_data
msgprompt_data: .asciiz "Positive integer: "
msgres1_data: .asciiz "The value of factorial("
msgres2_data: .asciiz ") is "
# every function call has a stack segment of 12 bytes, or 3 words.
# the space is reserved as follows:
# 0($sp) is reserved for the initial value given to this call
# 4($sp) is the space reserved for a return value
# 8($sp) is the space reserved for the return address.
# calls may manipulate their parent's data, but parents may not
# manipulate their child's data.
# i.e: if we have a call A who has a child call B:
# B may run:
# sw $t0, 16($sp)
# which would store data from $t0 into the parent's return value register
# A, however, should not(and, in all cases I can think of, cannot) manipulate
# any data that belongs to a child call.
.text
main:
la $sp, 0x300000
# printing the prompt
#printf("Positive integer: ");
la $t0, msgprompt # load address of msgprompt into $t0
lw $a0, 0($t0) # load data from address in $t0 into $a0
li $v0, 4 # call code for print_string
syscall # run the print_string syscall
# reading the input int
# scanf("%d", &number);
li $v0, 5 # call code for read_int
syscall # run the read_int syscall
move $t0, $v0 # store input in $t0
move $a0, $t0 # move input to argument register $a0
addi $sp, $sp, -12 # move stackpointer up 3 words
sw $t0, 0($sp) # store input in top of stack
sw $ra, 8($sp) # store counter at bottom of stack
jal factorial # call factorial
# when we get here, we have the final return value in 4($sp)
lw $s0, 4($sp) # load final return val into $s0
# printf("The value of 'factorial(%d)' is: %d\n",
la $t1, msgres1 # load msgres1 address into $t1
lw $a0, 0($t1) # load msgres1_data value into $a0
li $v0, 4 # system call for print_string
syscall # print value of msgres1_data to screen
lw $a0, 0($sp) # load original value into $a0
li $v0, 1 # system call for print_int
syscall # print original value to screen
la $t2, msgres2 #load msgres2 address into $t1
lw $a0, 0($t2) # load msgres_data value into $a0
li $v0, 4 # system call for print_string
syscall # print value of msgres2_data to screen
move $a0, $s0 # move final return value from $s0 to $a0 for return
li $v0, 1 # system call for print_int
syscall # print final return value to screen
addi $sp, $sp, 12 # move stack pointer back down where we started
# return 0;
li $v0, 10 # system call for exit
syscall # exit!
.text
factorial:
# base case - still in parent's stack segment
lw $t0, 0($sp) # load input from top of stack into register $t0
#if (x == 0)
beq $t0, 0, returnOne # if $t0 is equal to 0, branch to returnOne
addi $t0, $t0, -1 # subtract 1 from $t0 if not equal to 0
# recursive case - move to this call's stack segment
addi $sp, $sp, -12 # move stack pointer up 3 words
sw $t0, 0($sp) # store current working number into the top of the stack segment
sw $ra, 8($sp) # store counter at bottom of stack segment
jal factorial # recursive call
# if we get here, then we have the child return value in 4($sp)
lw $ra, 8($sp) # load this call's $ra again(we just got back from a jump)
lw $t1, 4($sp) # load child's return value into $t1
lw $t2, 12($sp) # load parent's start value into $t2
# return x * factorial(x-1); (not the return statement, but the multiplication)
mul $t3, $t1, $t2 # multiply child's return value by parent's working value, store in $t3.
sw $t3, 16($sp) # take result(in $t3), store in parent's return value.
addi $sp, $sp, 12 # move stackpointer back down for the parent call
jr $ra # jump to parent call
.text
#return 1;
returnOne:
li $t0, 1 # load 1 into register $t0
sw $t0, 4($sp) # store 1 into the parent's return value register
jr $ra # jump to parent call
# Standard startup code. Invoke the routine "main" with arguments:
# main(argc, argv, envp)
#
.text
.globl __start
__start:
lw $a0, 0($sp) # argc
addiu $a1, $sp, 4 # argv
addiu $a2, $a1, 4 # envp
sll $v0, $a0, 2
addu $a2, $a2, $v0
jal main
nop
li $v0, 10
syscall # syscall 10 (exit)
.globl __eoth
__eoth: