As far as I know, program counter, $PC is hidden from users. It isn't one of the 32 registers.
I want to do the following, instead of using jal
$ra <= PC + 4
j my_function
# instead of jal my_function
Is is possible to do this? I know JAIL is a macro.
How can I jump to a function without using JAL and able to return to the next instruction of the caller? Thanks.
Thanks!
One supported method of "discovering" the PC in code is to use JAL
with a zero offset (to jump/link to the next instruction), then read
the PC out of R31.
Attempt:
..statements before jump...
jal $0
addi $t3, $ra, 4
..statements when function returns..
I end up in infinite loop.
Doing a JAL to find out the PC is quite useless since you can use that JAL to do the jump... One way to do it is using LA pseudo-instruction, something like:
la $ra, current
addiu $ra, $ra, 8
current:
j example
nop
return:
j return
nop
example:
jr $ra
nop
Related
I have a mini bank program written that goes calls multiple functions inside the subrouting deposit, this is the subroutine
deposit:
addi $sp, $sp, -8 #save space on stack
addi $s3, $0, 1 #trigger s3
sw $s3, 0($sp)
sw $ra, 4($sp)
.....
jal AsciiConvert #convert ascii of deposited amount to integer
beq $v0, $0, Err_ACC #if no value to be deposited was inputed print error message
beq $t0, $0, deposit_checking #if check exists returns a 0
beq $t0, 1, deposit_saving #if check exists returns a 1
jal printarray
lw $s3, 0($sp)
lw $ra, 4($sp) # reload $ra so we can return to caller
addi $sp, $sp, 8 # restore $sp, freeing the allocated space
jr $ra
deposit_checking:
... arithmetic operations...
jr $ra
the ascii convert subroutine:
AsciiConvert:
...normal arithemtics...
j ConvertOP
ConvertOP:
lb $s0, 0($a1)
beq $s0, $0, endConvert #end at null terminating
beq $s0,32,endConvert #if found a space
addi $s0, $s0, -48 #convert to int
mul $s2, $s1, 10 #multiply sum by 10
add $s2, $s2, $s0 #sum = sum + previous number
add $s1, $s2, $0 #s1 holds previous value
addi $a1, $a1, 1 #increment adress
add $v0, $s2, $0 #store the number in the return adress
j ConvertOP
endConvert:
jr $ra
When I go into deposit, I jal AsciiConvert and then I go into the deposit_Checking subroutine, however the return address of that deposit_Checking returns me back to the line of jal AsciiConvert and not to the line where I called the deposit_Checking subroutine, leading to an infinite loop between Ascii convert subroutine and deposit_Checking subroutine...can someone please help me?
deposit_checking looks like a subroutine, and you identify it as a subroutine in your post, but, we don't enter subroutines with beq instruction, you're supposed to use jal to call a subroutine.
In machine code, the return address, $ra for MIPS, is effectively a parameter to the subroutine — it tells the subroutine where to resume execution in the caller, where to return to. There are several ways to set the $ra register with a meaningful return address, though of course jal is by far the most common way.
beq transfers control of the processor to the target label (when eq is true) by changing the program counter (pc) though does not provide a (new) $ra value.
By not setting $ra to a new value, its old value is retained. Since the $ra register was last set by the jal AsciiConvert, the jr $ra for the other function goes back there, none the wiser that this was not the right call — as it is the caller's job to set that parameter properly.
And while some instruction sets allow calling on a condition, we wouldn't necessarily want all beqs to capture a return address, because then any function that used beq would have to concern itself with preserving $ra.
Let's also note that these behaviors are all visible during debugging. jr $ra, for example will transfer control to whatever machine instruction is addressed by the value in the $ra register. When you first enter a subroutine/function, there should be a proper return address parameter provided in $ra register, and by the time you get to the end of the function with its final instruction jr $ra, if the value in the $ra register's value has changed from entry, then it won't go back to where it was called from — so we'd be looking for somewhere in between that changes $ra, without restoring it back.
I've been trying to learn how to program with Assembly MIPS32 on Mars.
I got a question though, i want to make a function (i don't know if that is how its called) that returns an integer.
Basically i would like to make a method that asks the user to insert a number and add that number with another one that was previously requested
This is what i came up with but i don't know how to return the value to $s2
New:
la $a0, prompt2 # Print string "Input: "
li $v0,4
syscall
li $v0,5 #Read int x
syscall
jr $ra
Add:
j New
add $s1, $s1, $s2 #add Int $s1 and $s2 and save them to $s1
j loop
if anyone has any suggestions or fixes please respond.
Thanks in advance
It is common on MIPS processors to use $v0 for function return values. Of course, you're free to come up with any calling convention you want for your own functions, but unless you have a very good reason not to I'd say stick with $v0 for return values.
Also, to call a function you'd typically use jal, because that's what sets up the $ra register so that you later can return with jr $ra.
If you don't already have it, I suggest that you download MIPS32™ Architecture For Programmers Volume II: The MIPS32™ Instruction Set and read up on how jal and jr work (or any other instruction that you feel that you don't fully understand).
So the code you described in your question could look something like:
New:
la $a0, prompt2 # Print string "Input: "
li $v0,4
syscall
li $v0,5 #Read int x
syscall
# The result of syscall 5 is in $v0
jr $ra
Add:
jal New
add $s1, $s1, $v0 # add the value we just read to $s1
j loop
I am writing a function in MIPS that will divide two fractions stored in the a0-a3 registers, like so: (a0/a1) and (a2/a3). I already have a function that will multiply both fractions together, so all I need to do is switch the numerator and denominator on the second fraction and call my multiplication function. I have that part figured out, however I also need to make sure that the numerator retains the sign, meaning that if I flip the second fraction around, and it's a negative, then I need to multiply both the numerator and the denominator by -1. I have written a separate function to do this for me, but after calling that function I don't know how to jump back to where I was. Here is the code:
f_div:
#Flip the second fraction (a2/a3) -> (a3/a2)
add $t0, $a2, $0
add $a2, $a3, $0
add $a3, $t0, $0
ble $a3, $0, f_flipsign #Branch if $a3 <= 0
#I need to be able to jump back here from f_flipsign
#How do I arbitrarily jump back to a location in the program without
#direct access to the PC?
add $s0, $ra, $0 #Save $ra so I don't lose it when I jal f_mul
jal f_mul
add $ra, $s0, $0 #Put the original $ra back
# Also, is there a better way to do this ^
jr $ra #Jump back to main
f_flipsign:
li $t0, -1
mult $a2, $t0
mflo $a2
mult $a3, $t0
mflo $a2
jr ? #How do I make this jump back to the middle of f_div?
I have researched and studied for hours today and I can't seem to figure this out. I understand how these instructions are formatted, I just need to know how to accomplish this one thing. Any help is greatly appreciated, thank you for taking your time.
If your f_flipsign subroutine is in fact some code you just need to execute only in that case, then maybe it does not have to be a subroutine at all and just change the branch condition and add the flip sign code there.
In that case, just change the ble to a bgt to skip the flip code, e.g.:
bgt $a3, $0, dont_flip #Branch if $a3 > 0
# Your code to flip sign
li $t0, -1
mult $a2, $t0
mflo $a2
mult $a3, $t0
mflo $a2
dont_flip:
# Code continues here (whether it flipped sign or not)
If flip_sign is a subroutine that may be called from many places, then you should use jal-code-jr, but you have to preserve $ra somewhere (usually the stack) so that it is not lost when calling the subroutine.
Assuming you have preserved $ra, then you would write something like this:
bgt $a3, $0, continue #Branch if $a3 > 0
jal f_flipsign
continue:
# Code continues here (whether it flipped sign or not)
and in your f_flipsign subroutine end with:
jr $ra
I'd like to pass a character as an argument to a function in MIPS. Do I do this by storing the character into register $a0, use jal to move to the function, then extract what's in $a0 into a separate register using lw?
If someone could give me an example of passing an argument or two in MIPS, I'd really appreciate it. I've found a lot of info on the MIPS calling conventions, but not any simple and succinct examples.
No need to use lw which is for extracting words from memory. You can simply use $a0 in the sub-routine.
Take a look at this example of a "print-char" function:
.text
main:
#save $ra on stack
addi $sp $sp -4
sw $fp 0($sp)
move $fp $sp
addi $sp $sp -4
sw $ra -4($fp)
#call sub-routine
addi $a0 $zero 'A'
jal printchar
#restore and shrink stack
lw $ra -4($fp)
lw $fp 0($fp)
addi $sp $sp 8
jr $ra
#prints a char and then a new line
printchar:
#call print-char syscall
addi $v0 $zero 11
syscall
addi $a0 $zero 10
syscall
jr $ra
As demonstrated, you the value of the $a0 register is just used in the sub-routine as it returns the value that it was given before the jal.
Also demonstrated is proper expansion and shrinking of the stack as is necessary for calling a sub-routing. As you will notice, the sub-routine does not perform this operation as it does not call a sub-routine and there-fore does not need to save the $ra. Stack manipulations would also be required in the sub-routine if it were to use an $s register as the MIPS calling convention specifies these as callee saved.
a very easy way to go about it would be to load the argument in a temp register and then just use:
move $a0,$t0
this way the argument stored in the temp register will be given as an argument
The following is my code in MIPS to calculate the sum of odd integers using a while loop.
.data
num: .space 4
.text
.globl main
main:
li $t1, 1
li $t2, 9 # make $t2 9 to break the loop
li $t3, 1
loop:
beq $t3, 11, Exit # check to see if $t3 = 11 if so exit
addi $t3, $t3, 2 # change $t3 to next odd number by adding 2
add $t1, $t1, $t3 # add $t3 to $t1 (1+3,...3+5...etc...)
j loop #jump back to the start of the loop
Exit:
li $v0, 1 # system call code to print an int
lw $a0, num # address of int to print
syscall # print the int
jr $ra #exit
This is my first real experience with MIPS and I'm not sure what is going wrong in this code. I put the print inside the while loop to see if it was ever calculating, but the result is always 1.
So, my result at the end is just 111111.
Edit: Removed the prints inside of the loop with the same result.
And OS is Windows 7 64x
Update: Having num as a variable was over complicating things. Code has been revised as follow and works. Thank you for the help!
enter code here
.data
.text
.globl main
main:
addi $t1, $0, 1
addi $t2, $0, 3
loop: bge $t2, 11, Exit # check to see if $t3 >= 11 if so exit
add $t1, $t1, $t2 # add $t2 to $t1 (1+3,...3+5...etc...)
addi $t2, $t2, 2 # change $t2 to next odd number by adding 2
j loop #jump back to the start of the loop
Exit:
li $v0, 1 # system call code to print an int
move $a0,$t1 # address of int to print
syscall # print the int
jr $ra #exit
la $t1, num
You're clearly getting into trouble here, since you're overwriting your accumulator with the address of num every time you're making the syscall. You're losing the current state of your calculation each time.
You'll need to either save your registers, or simply use different ones. Since I don't know what OS it is that you're using, I don't know if you more generally need to save registers over a syscall, but that could also be a source of errors.
I did similar problems for an architecture class and this seemed to be a recurrent problem among all students. When facing problems similar to this our professor's recommendation was to use a different register to temporarily store the register's address to avoid overwriting other desired values from our most commonly used regs.