MIPS: Calculating Displacement - mips

Working through some problems and I am confused by calculating the displacement.
Some examples are:
top:
addi $s2, $s2, -1
addi $s1, $s1, 1
bne $s2, $0, top
Assume top has the value of 0x1000 0008.
top: bne $s1, $s2, end
addi $s1, $s1, 1
end: j top
Assume top has the value of 0x1000 0008.
What is the displacement in the bne instructions?
Can anyone explain how to calculate these? Thanks.

The absolute address doesn't matter. What matters is the distance between the jump and its target.
To calculate the offset, calculate the distance between jump target and the instruction following the bne. In your first example the distance would be 12 bytes because there are 3 instructions between the target label and the instruction following the bne, and each instruction is 4 bytes in size. And since this is a backwards jump it has to be a negative offset, i.e. -12.
Since instructions need to be word aligned (4 bytes), the offset stored in the instruction word is shifted 2 bits to the right (since the two least significant bits always will be 00 anyway). This is an arithmetic shift, meaning the sign bit is preserved. So we need to take -12 and do an arithmetic right shift by 2 bits to get the actual offset that gets stored in the instruction word. This gets easier if we view -12 in hexadecimal form, which would be 0xFFF4. Shifting 0xFFF4 gives us 0xFFFD, which is what we'd put in the instruction word.
In your second example the offset will be positive, but apart from that the way you calculate it is exactly the same.

Related

How to translate MIPS into C and how to reduce MIPS instructions?

Supposing that f, g, h, i are stored in $s0~$s4 respectively and the base addresses of arrays A and B are in $S6 and $S7.
sll $t0, $s0, 2
add $t0, $s6, $t0
sll $tl, $sl, 2
add $tl, $s7, $tl
lw $s0, 0($t0)
addi $t2 , $t0, 4
lw $t0, 0($t2)
add $t0, $t0, $s0
SW $t0, 0($tl)
I'm not familiar with MIPS so I Wonder how to translate MIPS into C and how to minimize these MIPS instructions?
how to translate MIPS into C
You recognize the patterns, here for array indexing / array element access.
On a byte addressable machine (all modern hardware), a 4-byte integer occupies 4 bytes in memory, and each of those bytes has a unique memory address.  Because of the way the hardware works, we only use one of those 4 addresses to refer to the whole 4-byte integer, namely we use the lowest address among the 4.  The hardware can load a 4-byte integer from memory given that one address (the lowest).
Since each 4-byte integer in memory occupies 4 addresses, in an array of 4-byte integers, the memory address of the first element and the memory address of the second element are 4 addresses apart even though are sequential index positions (i.e. they are only 1 index position apart).
The formula for indexing a 4-byte integer array, then is to convert the index into a byte offset, then add the byte offset to the base address of the array.  The first part of that: converting an index to a byte offset, is sometimes referred to as "scaling".  Scaling is conceptually done by multiplication, so in A[i], i needs to be scaled by the size of the array elements of A.  If 4-byte integers that means scaling (multiplying) the index by 4.  A quick way of doing that is shifting by 2 bit positions, which has the same effect as multiplying by 4.
The C language automatically scales when doing array references, whereas assembly language requires explicit scaling.  C can do this because it knows the type of the array, whereas assembly language does not.
In C we can do expressions like A[i].  The C language allows us to break that down somewhat into *(A+i), which separates the pointer arithmetic addition A+i from the dereferencing of that sum, dereferencing with the unary indirection operator, *.  As previously mentioned, C automatically scales, so A+i becomes the equivalent of A+i*4, in which we can substitute shifting for multiplication: A+(i<<2).
Next, we need to know if the dereference is for read or for write.  When A[i] is accessed for its value, we will see it on what we call the "right hand side" of an assignment operator, as in ... = A[i].  When A[i] is access to update/store a value, we will see it on what we call the left hand side of an assignment operator, as in A[i] = ....
So, the sequence for doing A[i] for read (right hand side) in C is the following in assembly:
sll $temp1, $i, 2
addu $temp2, $A, $temp1
lw $temp3, 0($temp1)
Where $tempN is some register (usually a designated temporary) chosen to hold an intermediate value.  Since multiple instructions are needed to accomplish anything, sequences of instructions are interconnected with registers that hold the intermediate states.  And also, in assembly we name registers, not variables, so in my above $i and $A should be a registers names representing those variables rather than variable names directly used.
The pattern for write/store array access is similar but ends with a sw instruction instead, to store some value into memory at the index position.
These instruction sequence are interconnected by the use of these registers, and the sequences can be interrupted or interspersed with other instructions — what we have to follow then is the above pattern by paying attention to to the register usages that interconnect them rather than the specific sequences.
In your sample code:
sll $t0, $s0, 2 # sourcing an index in $s0, scaling it into temp $t0
add $t0, $s6, $t0 # adding a base array in $s6, putting back into $t0
sll $tl, $sl, 2
add $tl, $s7, $tl
lw $s0, 0($t0) # accessing the value of $s6[$s0*4], aka A[f]
addi $t2 , $t0, 4
lw $t0, 0($t2)
add $t0, $t0, $s0
SW $t0, 0($tl)
We can see the pattern for a read access to an index in $s0, and an array in $s6, these, we are told, map to f and A, so those three instructions comprise A[f] to read a value from A at index f.
The rest are done similarly.  Your job is to use this knowledge to find the other array indexing patterns in the above sequence.  Find out how the results of the array indexing operations are used and you'll have the complete C code.
NOTE that the sample you've been given incorrectly uses add and addi when pointer arithmetic should use addu and addiu — we don't want signed integer overflow checking on pointer arithmetic, as pointers are unsigned.
One of the add instructions is not for pointer arithmetic, but should probably still have used addu if this is intended to be replicated in C, because the C language does not have a built in operator to trap on overflow.

Swap Bytes in MIPS

Lets say I have a hex value 0xfac3, and would like the result stored in my register to be 0xc3fa. How would I go about doing this? when I use rotate or shift I always have all of the 0's from the register in the middle of the two bytes. I need to get rid of those 0's. for example:
lw $t0, 0xfac3
ror $t0, $t0, 8
the resulting value I get is: c30000fa. I need to get rid of the 0's

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.

representing the addi $s1, $0, 4 instruction: write down the value of the control signals

Im doing a homework where I need to write down the value of the control signals for 5 instructions and am trying to figure out the sample first (code at the bottom). The 5 instructions I need to do are
Address Code Basic Source
0x00400014 0x12120004 beq $16,$18,0x0004 15 beq $s0, $s2, exit
0x00400018 0x8e080000 lw $8,0x0000($16) 16 lw $t0, ($s0)
0x0040001c 0x02118020 add $16,$16,$17 17 add $s0, $s0, $s1
0x00400020 0xae08fffc sw $8,0xfffc($16) 18 sw $t0, -4($s0)
0x00400024 0x08100005 j 0x00400014 19 j loop
And the example he did is for addi $s1,$0,4 . Right now I have this for it:
Address Code Basic Source
0x00400028 0x20110004 addi $16,$0,4 20 addi $s1, $0, 4
where I think the 4 in the basic column is incorrect. What would be the right answer?
Heres the sample he did for that, and below that is the diagram he is referring to with the control signals:
##--------------------------
# Example
# addi $s1, $0, 4
# Although not supported as in Figure 4.24, the instruction can be easily
# supported with minor changes in the control circuit.
instruction_address=0x00400028
instruction_encoding=0x20110004
OPcode=0b001000
Jump=0
Branch=0
Jump_address=0x00440010 # not used in this instruction
Branch_address=0x0040003C # not used in this instruction
Read_register_1=0b00000
Read_register_2=0b10001
Sign_extend_output=0x00000004
ALUSrc=1 # pick the value from sign_extend_output
ALUOp=0b00 # assume the same value as load/store instruction
ALU_control_input=0b0010 # add operation, as in load/store instruction
MemRead=0
MemWrite=0
MemtoReg=0 # select the ALU result
RegDst=0
Write_register=0b10001 #register number for $s1
RegWrite=1
##--------------------------
Lets examine the breakdown of the first instruction: beq $s0, $s2, exit.
The instruction address is given under the address column above: 0x00400014. You have the encoding as well: 0x12120004. The encoding is the machine instruction. Lets represent the instruction in binary: 000100 10000 10010 0000000000000100.
This is an I-type instruction. The first group of six bits is the opcode, the second group of five is the source register, the third group of five is the temporary register, and the last group of sixteen is the immediate value.
The opcode is then 0b000100. Since this is an I-type instruction, we aren't jumping to a target, thus the Jump signal is 0. However, we are branching, so the Branch signal is 1.
To find the Jump_Address, even though it is ignored, examine the the least significant 26 bits: 10000 10010 0000000000000100. Since addresses are word-aligned, we can enlarge the range of reachable addresses by having the jump offsets be the signed difference between the next instruction and target address. In other words, if my target address is 8 bytes away from the next instruction (PC-relative addressing), I'll use 2 to represent the offset. And this is why we must shift the offset 2 bits to the left. So we end up with Jump_Address = 10 00010 01000 0000000000010000 or 0x8480010.
To find the Branch_Address, which will be used, examine the least significant 16 bits: 0000000000000100. That's sign extended and shifted 2 bits to the left to get: 0000000000000000 0000000000010000 or 0x00000010. This immediate value will be added to the program counter, which points to the next instruction: 0x00400018. So we finally end with Branch_Address = 0x00400028. I'm assuming the exit label points to the next instruction after the five you've posted above, right after the j instruction.
The registers are straightforward. Read_register_1 = 0b10000 and Read_register_2 = 0b10010.
The Sign_extend_output is just the immediate field sign-extended: 0x00000004.
On to the ALU control signals. ALUSrc controls the multiplexer between the register file and ALU. Since a beq instruction requires the use of two registers, we need to select the Read data 2 register from the register file. We aren't using the immediate field for an ALU computation, like with the addi instruction. Therefore, the ALUSrc is 0.
The ALUOp and ALU_control_input are hard-wired values that are created from the opcode. ALUOp = 0b01 and ALU_control_input = 0b0110. Pg. 323 of Computer Organization and Design, 4th. Edition Revised by Hennessey and Patterson and this web page have a table with the appropriate control signals for a beq instruction. Pg. 318 has a table with the ALU control bit mappings.
MemRead and MemWrite are 0 since we aren't accessing memory; MemToReg is X (don't care) since MemWrite is 0; RegWrite is 0 since we aren't writing to the register file; RegDst is X since RegWrite is 0; and lastly, to find Write_register, take bits 16-20 (look at the multiplexer between the instruction memory and register file), which are 0b10010.

Finding offset from a code snippet

I am a bit stuck up with the following question,
Consider the following MIPS code and answer the questions that follow.
addi $t1, $s0, 400
loop: lw $s1, 0($s0)
add $s2, $s2, $s1
lw $s1, 4($s0)
add $s2, $s2, $s1
addi $s0, $s0, 8
bne $t1, $s0, loop
What value is the label loop translated to in the conditional branch
instruction?
Now I know the mathematical formula for Branch Target Address. But here as memory addressing is not done so I found out the offset by counting the lines between the target address and PC. This gives the answer to be 7 (word offset). Am I right with this approach?
A quick experiment with MARS simulator http://courses.missouristate.edu/KenVollmar/MARS/download.htm gave me the answer-6, -5 for number of lines difference and another -1 because PC is increased by 1 after the instruction.
AFAIK, I'm afraid not.
As MIPS instruction reference says:
An 18-bit signed offset (the 16-bit offset field shifted left 2 bits)
is added to the address of the instruction following the branch (not
the branch itself), in the branch delay slot, to form a PC-relative
effective target address.
So as I understand, the distance from the branch instruction to the loop label is negative (because the label is before the branch, thus the address is lower). The distance is calculated in number of words (hence the 2 bits left shift). As all MIPS instructions are 4 bytes, this would be 6 instructions before, hence -6 is the value that should appear in the branch instruction offset (lower half-word). In binary: 1111 1111 1111 1010 (two's complement). In hexadecimal: FFFA.
Checked with simulator and seems that my reasoning is correct since the instruction is coded as 0x1530FFFA.