writing iteration in MIPS - mips

I am writing this program in MIPS to calculate 2 to a power given by me and sum the sequential powers down to 0. For example, if I put 4 in $a0 like in the code below, I want it to calculate (4^2)+(3^2)+(2^2)+(1^2) which should come out to be 15. It should stop before it reaches zero. This is what I have written so far
main:
addi $a0, $zero, 4 #put k in $a0 in this case
addi $a1, $zero, 0 #put 0 in current sum
addi $v1, $v1, 0 #tally the total in $v1
for:
lw $10, $a0 #load k into reg $10
lw $11, $a1 #load sum into $11
addi $10, $10, -1 #subtracts 1 from k
li $9, 0 #sets i in for loop ($9) to 0
done:
li $v0, 10
syscall
I'm new to MIPS and could use a lot of help on finishing this, I know I want to use a for loop, but I don't know how to go through it while subtracting 1 from k and also calculating the sum. How would I bring 2 to a power of k, because I guess there is no power operation in mips. At this point in the course I can only use add, sub, and, or, slt, addi, j, beq, lw, sw, and sll. Can you not use a constant when using sub? Thank you for any help

A power is a multiplication, and a multiplication is a sum. So you can write a function that does a multiplication by adding, and another function that does power by multiplicating. For example, the multiplication function:
multiply: # $a0 first factor, $a1 second factor, $v0 result of multiplication
or $t0, $zr, $zr
or $t1, $a1, $a1
or $t3, $zr, $zr
loop:
beq $t1, $zr, end
add $t0, $t0, $a0
addi $t1, $t1, -1
j loop
nop
end:
or $v0, $t0, $0
jr $ra
nop
(Please note I haven't tested this, and this will not work with negative numbers)
As a side note, you have MUL instruction as well, but I don't know if you saw that already.

I think the concept your instructor is trying to show you is that sll, in effect, multiplies by 2. You've got to think in binary. For example, let's start with 1:
0000 0000 0000 0000 0000 0000 0000 0001
Do a 'sll' on that, and what do you end up with? 0010 = 2. sll again and you get 0100 = 4. And so on, until you shift all the way over and have 0x80000000.
So the answer to the question, "How would I bring 2 to a power of k?", is simpler than you might have thought: you shift 1 by k.
# $t0 contains 'k', the amount we want to shift by
addi $t1, $zero, 1
sllv $t3, $t1, $t0
Note: I had to double-check that you can shift by a variable amount, but this link says sllv is valid. However, since it's not in your list of allowed functions, you'll have to do sll $t1, $t1, 1 in a loop. (Be sure to put your check at the start of the loop, in case the amount you want to shift by is zero!)

Quick question:
Are you doing 2^4+2^3.. etc? or 4^2+3^2 etc?
Just a quick note:
2^4+2^3+2^2+2^1+2^0 = 15.
4^2+3^2+2^2+2^1 != 15.
(If it is the first, you do need 2^0 because you need to contemplate for odd numbers, 2^0 = 1. This is how we get odd numbers in binary.)
In this case, the answer about left shift (lls) is correct. When you left shift a binary number you increase the exponent of base 2 by 1.
so:
0001 = 1
0010 = 2
0100 = 4
1000 = 8
These summed give you 15.
So you could shift left, and add the result of each shift to a register.

Related

MIPS multu overflow

As part of a hex to decimal program I'm writing in MIPS using QT spim I load the value of 16^7 (268435456) into a generic register. I then multiply this number by 1-15 depending on the character, add the result to a running total, and divide 16^7 by 16.
However, I encounter what I assume to be overflow issues when multiplying 268435456. For instance, the code
li $t0, 10
li $t1, 268435456
multu $t0, $t1
mflo $t2
li $v0, 10
syscall
Which is intended to multiply 268435456 by 10 and store the result in $t2 stores -1610612736 instead of 2684354560. Any ideas on how to fix this code to store the correct value?
multu is the unsigned version of mult, which means that it is not going to generate an overflow because the result is taken as an unsigned number. When you show the value through a syscall it is taken as a signed number, so the signed value is shown (which in this case is negative).
You didn't give much info regarding what you are trying to achieve, but if you mean to perform a signed multiplication (meaning you want to keep the sign of the result) and store the result in a 32 bit register then you are going to run into limitations. If this is the case you will need to check whether the operation is possible within these bounds before printing the results, meaning you have to check whether the overflow happened:
li $t0, 10
li $t1, 268435456
multu $t0, $t1
mflo $t2
li $t3, 31
srl $t0, $t0, $t3
srl $t1, $t1, $t3
srl $t2, $t2, $t3
xor $t0, $t0, $t1
xor $t0, $t0, $t2
bgtz $t0, noOperationRoutine
li $v0, 10
syscall
noOperationRoutine:
....
The above solution performs a check on the leftmost bit (obtained by shifting right by 31 bits), which is indicative of the sign (1 for negative and 0 for positive). If one and only one of the operands is negative, then the result should be negative. If both are negative or positive, then the result should be positive. I think you can see how the two xors perform this check.
If you want to work with results that are bigger than the maximum size of a signed number then you have to keep in mind that mult stores the low part of the result in LO and the high part in HI. At this point you are going to deal with 64 bits numbers, which occupy two words/registers each.

How to implement NOT operation in MIPS?

I am new in MIPS, and I am trying to judge whether each char in a string is an alpha. I used the ASCII code to help me to judge it, while I found there is no instruction representing larger than meaning. So I try to implement a not operation from the instructions I have known. Here's part of my code:
isAlpha:
sltiu $t0, $s2, 123
sltiu $t1, $s2, 97
nor $t1, $t1, $t1
and $t0, $t0, $t1
sltiu $t2, $s2, 107
sltiu $t3, $s2, 81
nor $t3, $t3, $t3
and $t2, $t2, $t3
or $t0, $t0, $t2
bne $t0, $zero, countAlpha
jr $ra
However, I could not get the result I want. I set a breakpoint and found that my not operation seems to have some problems:
In my exception, $t1 should be 1 and $t2 should be 0, while the reality is not.
Where's the wrong place in my code? Is there any way to implement not operation in MIPS? Or is there a better way to implement larger than meaning in MIPS? Thanks in advance.
You have a not operation here:
nor $t1, $t1, $t1
Often you can just type the not mnemonic and your assembler will interpret it as a pseudo instruction
I think you want an exclusive or operation to tell you if your input is just one of less than 123 and less than 97.
Something like this (totally untested)
isAlpha:
sltiu $t0, $s2, 123
sltiu $t1, $s2, 97
xor $t0, $t0, $t1
sltiu $t2, $s2, 107
sltiu $t3, $s2, 81
xor $t2, $t2, $t3
or $t0, $t0, $t2
bne $t0, $zero, countAlpha
jr $ra
You can get the effect of gt by reversing the arguments to slt*:
sltu $v0,$t0,$t1 # v0 = $t0 < $t1 (what you have)
sltu $v0,$t1,$t0 # v0 = $t0 > $t1 (what you want)
Note that ge or le is a bit trickier. Consider the various branch pseudo ops like: blt, bge, bgt, bge [they generate slt* followed by either beq or bne]. They're usually easier to work with.
xor can do bitwise negation. The not pseudo-op will generate a nor.
Below is some code that does what you want. Note that it is similar to the code sfi posted, but has an additional [and necessary] and during the range check to prevent a false positive.
For example, without the and, the range check for a-z will report true on anything higher than z (e.g. 0x7B aka {). This is because both slt instructions will generate 0. But, xor of two zeros is 1. So, the xor result must be anded against the high range value slt result
# isAlpha -- decide if char is alpha
#
# RETURNS:
# v0 -- 1=is alpha
# s6 -- count of alpha chars
#
# arguments:
# s2 -- character to test
#
# registers:
# t0 -- holds lt 'a/A'? value
# t1 -- holds lt 'a/A' + 1? value
# t2 -- bool for within 'a-z'
# t3 -- bool for within 'A-Z'
isAlpha:
# within a-z range
sltiu $t0,$s2,0x61 # lt 'a'? (i.e. not a-z)
sltiu $t1,$s2,0x7B # lt 'z' + 1? (i.e. le 'z')
xor $t2,$t0,$t1 # want one but not both
and $t2,$t2,$t1 # want only lt 'z' + 1
# within A-Z range
sltiu $t0,$s2,0x41 # lt 'A'? (i.e. not A-Z)
sltiu $t1,$s2,0x5C # lt 'Z' + 1? (i.e. le 'Z')
xor $t3,$t0,$t1 # want one but not both
and $t3,$t3,$t1 # want only lt 'Z' + 1
or $v0,$t2,$t3 # decide if alpha
add $s6,$s6,$v0 # increment alpha count
jr $ra

Replacing left logical shifts with other instructions in MIPS

So let's say that we have two registers $s0 and $s1. Register $s0 can take values from 0 to 31 and is used to show how many bits register $s1 will be shifted left. How can i do this without using sll or srl. I know that left logical shift by n bits is the same as multiplying with 2^n. This exercise gives a hint that this can be done with 4 instructions only. Both registers are 32-bit.
You can add $s1 to itself $s0 times (each time it will multiply its value by 2 which shifts its bits left once).
The code would look like this:
beqz $s0, end
loop:
add $s1, $s1, $s1
subu $s0, $s0, 1
bnez $s0, loop
end:
The first conditional branch is to ensure no action is taken to $s1 when $s0 holds value zero.

Checking even or odd on mips using Shift operators and bne

I know I can check whether an integer is even if I shift right by 1, then shift left by 1 and the number stays as the original number.
My code isn't working as intended. This is my first time using MIPS and any help would be appreciated. I want to let the user input any number and my code will tell the user whether it is an even number.
.data
msg: .asciiz "even"
.text
.globl main
main:
li $v0, 5
syscall
or $t0, $0, $v0 # Register $t0 gets input
srl $t1 , $t0 , 1 #shift right by 1
syscall
sll $t1 , $t0 , 1 #shift left by 1
syscall
bne $t1, $t0, msg # if t1 equals t2 then it prints out even
syscall
There are a couple of strange things you're doing, and one easy-fix bug. But until you get the strange things fixed, you're going to be hozed.
First, like #markgz said, what are those syscalls supposed to be doing? Get rid of all of them except the one where you're getting a value from the user. Otherwise, you're going to get a number from the user over and over.
Second, branching to msg makes no sense. The memory at msg is an ASCIIZ string containing the characters m, s, g, and \0. These are not the 32 bits you are looking for. You need to branch to an instruction. I'm going to assume that labels isEven and isOdd exist somewhere.
The bug is what #Konrad said. Omitting the strange syscalls, your code is doing this:
srl $t1, $t0, 1 # shift *original val* right by 1 *and put in $t1*
sll $t1, $t0, 1 # shift *original val* left by 1 *and put in $t1*
bne $t1, $t0, isOdd # *will always be true because we just changed $t1 to be <> $t0*
So if you follow #Konrad's suggestion, it would look like this:
srl $t1, $t0, 1 # shift *original val* right by 1
sll $t1, $t1, 1 # shift *modified val* left by 1
bne $t1, $t0, isOdd # *Now we know if the value changed*
Note that I'm branching to isOdd. You claimed that if the values are different, then the number is even. That's incorrect.
As an aside, my favorite way to do this would be a little bit different:
andi $t1, $t0, 0x01
beq $t1, $zero, isEven
I'll leave it to you to figure out what I'm doing. One thing about your method: it fails if the most significant bit is set (which it will be if it's a negative signed integer).
There are so many bugs in your code.
srl $t1 , $t0 , 1 #shift right by 1
syscall # why you are using syscall here ??
sll $t1 , $t0 , 1 #shift left by 1--and should be sll $t1, $t1, 1
syscall #and why also here ??
bne $t1, $t0, msg #wrong ,here you need to pass label as third operand for offset
li $v0,4 # code 4 == print string
la $a0,string # $a0 == address of the string
syscall
Note: in spite of this you can just do & operation on any data with 0x1 and then check for the result is 0 (even) or 1 (odd)
I want also to share this link for programming tutorial on mips
http://chortle.ccsu.edu/AssemblyTutorial/index.html

MIPS - loading individual bits

Fellow SO users,
If I have a value, say 1010 saved in memory, how do I load each of the bits starting from the MSB to the LSB one at a time? By this I mean,
while loop
load $t1 with 1
shift some number by the amount indicated by the value in $t1 (1)
load $t1 with 0
shift some number by the amount indicated by the value in $t1 (0)
do the same until you load the LSB which is 0
start again at MSB (rotate right?)
end loop
I need some tips on how to declare this value in memory in the .data segment first. Do I declare this as:
value: .byte 00001010
And how do I load the individual bits into registers?
Kind regards
You can do this with the SLT instruction described here. Here is some pseudocode:
lbu $s0, value # load the byte
sll $s0, $s0, 24 # shift it into the upper byte of $s0
for (i = 0; i < 8; i++) {
# $t0 is set to 1 if the sign bit (i.e. bit 31) of $s0 is set
slt $t0, $s0, $zero
# $t0 now holds the value of the ith bit of value
. . . . # do something with $t0
sll $s0, $s0, 1 # shift on to the next bit
}
Implementing the for loop in assembler is left as an exercise for the reader.