How do I calculate the sum of odd positive integers in MIPS? I have a MIPS simulator at home and I use to book to help verify my work. My university has a computer lab that has hardware provided by an outside company. The idea I suppose is that the University "pimps out" the hardware to students through the classes. Part of the problem for me is that I want to verify my code work properly, while using the board at school but it seems easier to verify the code works at home. Anyway, I think the code should read something like this:
andi $t8, $s0, 1 #value from $s0 and add 1 to it. Place in $t8 register
bnez $t8 #This should determine if its odd
beqz $79 #This should determine if its even
even:
addi $t7, $t8, -1
bnez $t7, odd
odd:
addi $t6, $t7, -2
Rt6, loop
Is there an easier way to do this? I need to write a main routine in which at the end of the execution perform v0=the sum of odd positive integers between 1 and 1000. $t8 is my $v0 in this case. Any helpful suggestions would be considered very closely.
Here's some annotated code that does the sum of both odd and even values. It also has an example of a subroutine.
.data
array:
.word 17767, 9158, 39017, 18547
.word 56401, 23807, 37962, 22764
.word 7977, 31949, 22714, 55211
.word 16882, 7931, 43491, 57670
.word 124, 25282, 2132, 10232
.word 8987, 59880, 52711, 17293
.word 3958, 9562, 63790, 29283
.word 49715, 55199, 50377, 1946
.word 64358, 23858, 20493, 55223
.word 47665, 58456, 12451, 55642
arrend:
msg_odd: .asciiz "The sum of the odd numbers is: "
msg_even: .asciiz "The sum of the even numbers is: "
msg_nl: .asciiz "\n"
.text
.globl main
# main -- main program
#
# registers:
# t0 -- even sum
# t1 -- odd sum
# t2 -- current array value
# t3 -- isolation for even/odd bit
# t6 -- array pointer
# t7 -- array end pointer
main:
li $t0,0 # zero out even sum
li $t1,0 # zero out odd sum
la $t6,array # address of array start
la $t7,arrend # address of array end
main_loop:
bge $t6,$t7,main_done # are we done? if yes, fly
lw $t2,0($t6) # get value
addiu $t6,$t6,4 # point to next array element
andi $t3,$t2,1 # isolate LSB
beqz $t3,main_even # is is even? if yes, fly
add $t1,$t1,$t2 # add to odd sum
j main_loop
main_even:
add $t0,$t0,$t2 # add to even sum
j main_loop
main_done:
# output the even sum
la $a0,msg_even
move $a1,$t0
jal print
# output the odd sum
la $a0,msg_odd
move $a1,$t1
jal print
# terminate program
li $v0,10
syscall
# print -- output a number
#
# arguments:
# a0 -- pointer to message
# a1 -- number to output
print:
# output the message
la $v0,4
syscall
# output the number
li $v0,1
move $a0,$a1
syscall
# output a newline
la $a0,msg_nl
li $v0,4
syscall
jr $ra # return
If you'd like some tips on writing clean asm, based on my own experience, see my answer: MIPS linked list
I've used spim, QtSpim, and mars for simulators. Personally, I prefer mars where possible. See: http://courses.missouristate.edu/KenVollmar/mars/
Related
I am having a hard time figuring out where to start with this project. I am needing to write code in PLP that is a palindrome checker.
the task is to write a program that recieves a string of characters via UART, checks if this string is a palindrome, then uses a print function to print either"yes" of "no". I have been given a template that I am to follow when creating the program.
The template project file contains six function stubs that need to be implemented. five are called from the main loop and the sixth is called from "period_check: in the template file it contains descriptions of what each function needs to do and how it should be implemented. I have attempted to fill in some, however I do not think I am on the right track. Please help.
***** I have gotten this much code in, but it does not print out the right output****
it prints no for everything vs no for non palindromes and yes for palindrome.
.org 0x10000000
# Initializations
# NOTE: You may add initializations after line 10, but please do not
# remove or change the initializations to $sp, $s0, $s1, or $s2
li $sp, 0x10fffffc # Starting address of empty stack
li $s0, 0xf0000000 # UART base address
li $s1, array_ptr # Array head pointer
li $s2, array_ptr # Array tail pointer
####################################################################
# Do not make changes to the jump to main, the allocation of
# memory for the array, or the main loop
####################################################################
j main
nop
array_ptr: # Label pointing to 100 word array
.space 100
main:
jal poll_UART
nop
jal period_check
nop
jal space_check
nop
jal case_check
nop
jal array_push
nop
j main
nop
####################################################################
# ******************************************************************
####################################################################
# The "poll_UART" function should poll the status register of the UART.
# If the 2^1 bit position (ready bit) is set to 1 then it
# should copy the receive buffer's value into $v0 and send
# a clear status command (2^1) to the command register before
# returning (a return statement is already included). In order to
# receive full credit, $s0 must contain the base address of the UART
# and must be used with the appropriate offsets to access UART
# registers and buffers
poll_UART:
lw $t1, 4($s0)
li $t2, 0b10
and $t3, $t1, $t2
beq $t3, $0, main
nop
lw $v0, 8($s0)
sw $t2, 0($s0)
jr $ra
nop
# The "period_check" function should check if the current character ($v0)
# is a period ("."). If it is a period then the function should go to the
# label, "palindrome_check". If the character is not a period then it
# should use the included return.
period_check:
li $t0, 0x2E
beq $v0, $t0, palindrome_check
nop
# The "space_check" function should check if the current character ($v0)
# is a space (" "). If it is then it should jump to "main" so
# that it skips saving the space character. If not it should
# use the included return.
space_check:
li $t4, 0x20
beq $t4, $v0, main
jr $ra
nop
# The "case_check" function should perform a single inequality check.
# If the current character ($v0) is greater than the ASCII value of 'Z',
# which indicates the current character is lowercase, then it should convert
# the value of $v0 to the uppercase equivalent and then return. If the
# current character ($v0) is already uppercase (meaning the inequality
# mentioned before was not true) then the function should return without
# performing a conversion.
case_check:
li $t5, 0x5A
slt $t6, $v0, $t5
li $t7, 1
beq $t6, $t7, convert
convert:
addiu $v0, $v0, -32
jr $ra
nop
# The "array_push" function should save the current character ($v0) to the
# current location of the tail pointer, $s2. Then it should increment the
# tail pointer so that it points to the next element of the array. Last
# it should use the included return statement.
array_push:
sw $v0, 0($s2)
addiu, $s2, $s2, 4
jr $ra
nop
# The "palindrome_check" subroutine should be jumped to by the period
# check function if a period is encountered. This subroutine should contain
# a loop that traverses the array from the front towards the back (using the
# head pointer, $s1) and from the back towards the front(using the tail
# pointer, $s2). If the string is a palindrome then as the array is traversed
# the characters pointed to should be equal. If the characters are not equal
# then the string is not a palindrome and the print function should be used
# to print "No". If the pointers cross (i.e. the head pointer's address is
# greater than or equal to the tail pointer's address) and the compared
# characters are equal then the string is a palindrome and "Yes" should be
# printed.
#
# Remember to restore the head and tail pointers to the first element
# of the array before the subroutine jumps back to main to begin processing the
# next string. Also, keep in mind that because the tail pointer is updated at
# the end of "array_push" it technically points one element past the last
# character in the array. You will need to compensate for this by either
# decrementing the pointer once at the start of the array or using an offset
# from this pointer's address.
palindrome_check:
addiu $s2, $s2, -8
move $s3, $s1
subu $s6, $s2, $s3
beq $s6, $0, palindrome
nop
check_loop:
lw $s4, 0($s3)
lw $s5, 0($s2)
bne $s5, $t0, not_palindrome
nop
adjust_pointers:
addiu $s2, $s2, -4
addiu $s3, $s3, 4
slt $t8, $s3, $s2
bne $t8, $t0, check_loop
nop
j palindrome
nop
palindrome:
li $a0, 1
call project3_print
move $s2, $s1
j main
not_palindrome:
li $a0, 0
call project3_print
move $s2, $s1
j main
nop
Ok, this is just my opinion, but you are definitely not on the right track.
The control flow you're showing is problematic.
To see one reason why, try writing this same in C or any other language that you know. You won't be able to do it because of the non-local goto's that's using, where one procedure jumps (without calling) to another procedure.
Further, finding whether an input is a palindrome is not a fixed sequence of one-time steps that are executed on each input character.
You will (1) need to store the characters for later comparison, and (2) need a decision point where you can determine (and print) yes it is, or no it isn't. You don't have any control structure for that.
that recieves a string of characters via UART, checks if this string is a palindrome, then uses a print function to print either"yes" of "no".
Yes, your main should reflect the above description you've been given:
receive a string of characters
checks if this string is a palindrome
print either "yes" of "no"
In other words you might have something like:
int len = input_string();
if ( check_palindrome(len) ) {
print "yes";
else
print "no"
Suggest you write it in C or other language you know, then translate that to assembly.
Also consider that we some things we program are functions returning a value rather than procedures that don't return values. Returning a value so that main can take a different course of action (e.g. print yes vs. no) is much better than using non-local goto's to alter the flow of control from within a subroutine.
If your instruction/coursework has given you that main, and is recommending non-local goto's that would be very sad.
I feel for you and your classmates, as this is one of the worst examples of teaching assembly I've seen in a long long time.
array_ptr: # Label pointing to 100 word array
.space 100
The label name is misleading. This space is used as an array of words, not a pointer to an array. The storage reserved is 25 words, since .space operates in terms of bytes and words are 4 bytes each. So, the comment is just plain wrong.
The various "functions" called using jal are single use function, so there's really no need for functions in this assignment at all. The "functions" also are going to each other and back to main instead of returning properly like they would in structured programming. So, this is what we call spaghetti code — such code is difficult to reason over and one of the reasons that other languages don't even bother to offer this kind of flow control.
The array being used is storing whole words, when the input elements are only characters, so that's harmless but unnecessary.
beq $t6, $t7, convert
convert:
This control structure will never choose between two options, it will always convert. Why? Because in the case $t6 is true it will branch to convert: and in the case that $t6 is not true it will fall through to convert:, so same location, will run same code in either case.
You should be able to observe this during debugging.
Debugging Tips
Get to know your data. You should know the address of the array as you debug. You can find this during execution, e.g. look at a register after li ... array_ptr (btw, that opcode should be la, but no matter if it works). Otherwise you can observe the data section and its layout to find that out before running the first instruction.
Single step each line like one would to debug code in any other language, verifying program state between each line. In MIPS assembly, not much program state changes between lines so usually this is pretty simple — usually each instruction only changes one register or one memory location — but you must verify that such change is as you're expecting. Once the first part of the program is properly storing characters into the array, you can use the break point feature to stop at the palindrome check routine and single step only from there on.
Use the smallest possible input first, (in the most degenerate case that would be an empty string, but you may not be handling those so instead) might try a single letter input (should be a palindrome). Once that is working, try two letter input. As I said, first make sure that the character values are being placed into the array properly, and only when you've verified that's working, go on to debug the palindrome check code.
I just want to check my understanding of these two concepts is correct, as I have been trying to finish a project and while everything works to my expectations, it keeps narrowly failing the test cases and introducing a random value...
Basically, the objective of the project is to write out a branch instruction to console in this form:
BranchName $S, [$t, if applicable] 0xAbsoluteAddressOfBranchTargetInstruction
Edit: Clarification: I'm writing this in MIPS. The idea is I get a memory address in $a0 given to the program by my instructor's code (I write the function). The address is for the word containing a MIPS instruction. I'm to do the following:
Get instruction
Isolate instruction opcode and output its name to register (ie: opcode 5, output BNE), do nothing if it isn't a branch instruction.
Isolate $s, $t, and output as applicable (ie: no $t for bgez)
Use offset in the branch instruction to calculate its absolute address (the address of the target instruction following branch) and output in hex. For the purposes of this calculation, the address of the branch instruction ($a0) is assumed to be $pc.
IE:
BEQ $6, $9, 0x00100008
Firstly, is my understanding of branch calculation correct?
PC -> PC + 4
Lower 16 bits of instruction
<< 2 these lower bits
Add PC+4 and the left shifted lower 16 bits (only the lower 16 though).
Secondly, could somebody tell me which bits I need to isolate to know what kind of branch I'm dealing with? I think I have them (first 6 for BEQ/BNE, first 16 with $s masked out for others) but I wanted to double check.
Oh, and finally... should I expect deviation on SPIM from running it on an Intel x86 Windows system and an Intel x86 Linux system? I'm getting a stupid glitch and I cannot seem to isolate it from my hand-worked address calculations, but it only shows up when I run the test scripts my prof gave us on Linux (.sh); running directly in spim on either OS seems to work... provided my understanding of how to do the hand calculations (as listed above) is correct.
This is prefaced by my various comments.
Here is a sample program that does the address calculation correctly. It does not do the branch instruction type decode, so you'll have to combine parts of this and your version together.
Note that it uses the mars syscall 34 to print values in hex. This isn't available under spim, so you may need to output in decimal using syscall 1 or write your own hex value output function [if you haven't already]
.data
msg_best: .asciiz "correct target address: "
msg_tgt: .asciiz "current target address: "
msg_nl: .asciiz "\n"
.text
.globl main
main:
la $s0,inst # pointer to branch instruction
la $s1,einst # get end of instructions
subu $s1,$s1,$s0 # get number of bytes
srl $s1,$s1,2 # get number of instruction words
la $s2,loop # the correct target address
la $a0,msg_best
move $a1,$s2
jal printaddr
loop:
move $a0,$s0
jal showme # decode and print instruction
addiu $s0,$s0,4
sub $s1,$s1,1
bnez $s1,loop # more to do? yes, loop
li $v0,10
syscall
# branch instructions to decode
inst:
bne $s0,$s1,loop
beq $s0,$s1,loop
beqz $s1,loop
bnez $s1,loop
bgtz $s1,loop
bgez $s1,loop
bltz $s1,loop
blez $s1,loop
einst:
# showme -- decode and print data about instruction
#
# NOTE: this does _not_ decode the instruction type
#
# arguments:
# a0 -- instruction address
#
# registers:
# t5 -- raw instruction word
# t4 -- branch offset
# t3 -- absolute address of branch target
showme:
subu $sp,$sp,4
sw $ra,0($sp)
lw $t5,0($a0) # get inst word
addiu $t3,$a0,4 # get PC + 4
sll $t4,$t5,16 # shift offset left
sra $t4,$t4,16 # shift offset right (sign extend)
sll $t4,$t4,2 # get byte offset
addu $t3,$t3,$t4 # add in offset
# NOTE: as a diagnostic, we could compare t3 against s2 -- it should
# always match
la $a0,msg_tgt
move $a1,$t3
jal printaddr
lw $ra,0($sp)
addu $sp,$sp,4
jr $ra
# printaddr -- print address
#
# arguments:
# a0 -- message
# a1 -- address value
printaddr:
li $v0,4
syscall
# NOTE: only mars supports this syscall
# to use spim, use a syscall number of 1, which outputs in decimal and
# then hand convert
# or write your own hex output function
move $a0,$a1
li $v0,34 # output number in hex (mars _only_)
syscall
la $a0,msg_nl
li $v0,4
syscall
jr $ra
The 16 bit immediate value is sign-extended to 32 bits, then shifted. I don't know if that would affect your program; but, that's the only potential "mistake" I noticed.
I am trying to make a program on QtSpim that constantly takes new integers as input (through the console) and then prints them on the console only when they are even numbers. I set the input 5 as the exit case. The program compiles as it should and when I press the run button there doesn't seem to have a problem. The problem is when I try to type the input number, as the console doesn't respond to that (the number I just typed doesn't even appear on the screen).
Here is my code, I imagine the mistake must be in the first lines where the input number is read, but I can't find it:
.text
.globl __start
__start:
li $v0,5
syscall
move $t0,$v0
add $t1,$t0,$zero
addi $t2,$zero,5
LOOP: div $t0,$t0,2
bne $t0,$zero,LOOP
mfhi $t3
bne $t3,$zero,REPEAT
li $v0,1
move $a0,$t3
syscall
REPEAT:bne $t1,$t2,__start
li $v0,10
syscall
.data
The thing can be done in a much simpler way, using the bitwise and.
Every odd number will have the last bit set, which will make number & 1 equal to 1.
.text
.globl __start
__start:
li $t0, 5
loop:
move $v0, $t0 # set $v0 to 5: read integer
syscall # read in the number
andi $t1, $v0, 1 # check if it's divisible by 2
bnez $t1, check # if no, jump to a check for 5
move $a0, $v0 # if yes, print it
li $v0, 1 # set $v0 to 1: print integer
syscall # do the printing
j loop # continue
check:
bne $t0, $v0, loop # if the integer read is not equal to 5, run again
li $v0, 10
syscall # exit
Your method of checking for even numbers is incorrect. Whenever you enter a number >=1 you'll end up dividing 1 by 2 on the last iteration of your loop. And of course 1 MOD 2 is 1, so your code always thinks the number is odd.
A single division by 2 is sufficient to determine if the value is odd or even. But an AND operation would be even more efficient:
andi $t0,$t0,1 # if the least significant bit is set, the number is odd
bne $t0,$zero,REPEAT
After making that change you'll probably also have to change the printing code, since the value to print no longer is in $t3.
I have this program I just wrote:
countzeroes:
li $v0, 0 # count = 0
li $t0, 0 # int i = 0
li $v1, 1 # compare bit = 1
cz_loop:
bge $t0, 32, cz_exit # exit loop if i >= 32
andi $t1, $a0, 1 # bit = arg0 & 1
beq $t1, $v1, cz_skip # skip if bit = 1
addi $v0, $v0, 1 # adds 1 to count
cz_skip:
srl $a0, $a0, 1 # shifts input right by 1
add $t0, $t0, 1 # i++
j cz_loop
cz_exit:
jr $ra
Pretty simple, just computes the number of zeroes in a 32 bit word. I was wondering how the program knows how to return $v0 at the end? I know v0 and v1 are return registers, but I was wondering if those two are always returned. If not, how does the program know to return v0?
In addition, I know jr $ra jumps to the return address- but what does that mean?
Thanks for your help.
"how the program knows how to return $v0 at the end?"
It doesn't know, you're writing the "return" value in $v0, in fact you could return the "result or return values" in any available register such as the temporals, it's just a convention to use the $v0 register to return values (in MIPS).
"I was wondering if those two are always returned"
Remember that in any subroutine in your program you always have access to all registers, so there's not "restriction" about what register store values that can be semantically called "return values", so I could easily create a method that returns 3 numbers in $t0, $t1, $t2 but that's my choice, you can return values in the stack also, there are a lot of possibilities, and this depends and lays down on the good programming practices and also the calling conventions, here you can find the MIPS calling convention: https://courses.cs.washington.edu/courses/cse410/09sp/examples/MIPSCallingConventionsSummary.pdf
" jr $ra jumps to the return address- but what does that mean?"
The program is executed instruction by instruction(the program has an instruction pointer aka program counter), when you call a subroutine the address of the next instruction is being stored in the $ra register, then when you make jr $ra, the program execution returns to that address (the instruction pointer gets the value of $ra).
In MIPS there are three different jumps you'll see. j, jr & jal.
j: it is considered an unconditional jump. Simply just do:
j function
jr:aka jump to register. Exactly as the name sounds you jump to register. This is when you have a register already saved somewhere in your program you want to jump back to. It will usually look like so:
jr $ra
$ra being the register which had been previously set aside before your jal (see below) which will jump the program back to that address.
jal: aka Jump and link copies the address of the next instruction into the register and then jumps to the address label. In other words, both jal and jr are used in conjunction with each other mainly for going to functions which are usually placed after the exit calls.
Ex:
main:
#program
jal function
#continue with program
function:
#
#do something
#
jr $ra
Also, most helpful site when I started learning: http://logos.cs.uic.edu/366/notes/mips%20quick%20tutorial.htm
Some other quick hints that I wish someone told me when I started:
Always start with "main:"
Be wary of the difference between high/low registers in multiplying
and dividing integers
Always keep track of your registers because with so many $s and $t
and $f and $p and $a and $v registers working at one time things can
get messy very quickly.
I'm new in MIPS language and I don't understand how the functions (procedures) in the MIPS assembly language work. Here are but I will specify my problem :
What does:
jal
jr
$ra
mean in mips language and the important thing
How can we use them when we want to create a function or (procedure)?
Firstly, you might want to check this quick MIPS reference. It really helped me.
Secondly, to explain jal, jr and $ra. What jal <label> does is jump to the label label and store the program counter (think of it as the address of the current instruction) in the $ra register. Now, when you want to return from label to where you initially were, you just use jr $ra.
Here's an example:
.text
main:
li $t0, 1
jal procedure # call procedure
li $v0, 10
syscall
procedure:
li $t0, 3
jr $ra # return
You will notice when running this in a SPIM emulator that the value left in $t0 is 3, the one loaded in the so-called procedure.
Hope this helps.
1.the first two are instructions,the third it's kind of special register
jal=jump and link (Address of following instruction put in $ra,and jump to target address)
jr=jump to specify register
$ra=return address
we often use the instruction like this ...
jr $ra (Copy $ra to program counter)
it means return(jump) to the address saved in $ra .
2.
Here's an example function (procedure) in C
int main(){
x=addthem(a,b);
}
int addthem(int a, int b){
return a+b;
}
function in MIPS
.text
main: #assume value a is already in $t0, b in $t1
add $a0,$0,$t0 # it's the same function as move the value
add $a1,$0,$t1
jal addthem # call procedure
add $t3,$0,$v0 # move the return value from $v0 to where we want
syscall
addthem:
addi $sp,$sp,-4 # Moving Stack pointer
sw $t0, 0($sp) # Store previous value
add $t0,$a0,$a1 # Procedure Body
add $v0,$0,$t0 # Result
lw $t0, 0($sp) # Load previous value
addi $sp,$sp,4 # Moving Stack pointer
jr $ra # return (Copy $ra to PC)
You will want to read the System V Application Binary Interface, MIPS RISC Processor Supplement. This describes the conventions used for calling functions, in particular how the stack is managed and parameters are exchanged (there is no hardware stack in MIPS, everything is a matter of software conventions, and the ABI defines those conventions).
The document above assumes some basic knowledge of what MIPS instructions do, so you will also need the MIPS32 Architecture for Programmers, in particular volume II (instruction set), which describes the detailed effect of each instruction. But, do yourself a favor, download and read volume I (introduction) first.
The jal instruction is the "jump and link" opcode. It jumps at the target address (which is the address of the first opcode of the called procedure) while saving the current instruction pointer into the link register, which is register 31 (to be precise, it saves in register 31 the value x+8, where x is the address of the jal opcode itself).
jal: aka jump and link against any function name will redirect you to the required function.
jr $ra: It returns the value from a function that was called.
main function:
.data
x: .word 3 # initializing x to be 3
y: .word 5 # initializing y to be 5
answer: .word 0 # intialzing answer to be 0
prompt: .asciiz "Add X to Y: " # console prompt for final answer
.text
.globl main
.ent main
main:
lw $a0, x # pass arguments to function $a0 - x, $a1 - y
lw $a1, y
jal adds # adds function is called
sw $v0, answer # answer from the returned value
la $a0, prompt # displaying prompt on the console
li $v0, 4
syscall
lw $a0, answer # displaying final answer on the console
li $v0, 1
syscall
li $v0, 10 # end program
syscall
.end main
adds function:
.globl adds
.ent adds
adds: # adds function
li $v0, 0
add $v0, $a0, $a1 # adding arguments to the callee
jr $ra # return
.end adds