Arithmetic between double and integer in MIPS assembly - math

Below is a MIPS program that converts from Fahrenheit to Celsius.
I've defined 3 variables, one of which is a double and the rest are all integer. In order to perform division or multiplication between the variables, however, I had to convert the int variables to double later in the code.
The whole reason I declared the variables as int not float or double is because I'm literally storing one-digit decimal constants, and I wouldn't need to use two registers for that, asdouble requires. However, now I realise I'm facing two new problems:
1) I now need to do two extra steps for each variable (moving to cp1 and converting to double)
2) After the line mtc1.d $t1, $f6, the values become double and they get stored in two registers like normal doubles instead of one register like integers, hence missing my intention completely.
My question is, which way is better for memory and program efficiency? Declaring the variables as all of the same data type and shortening the number of instructions but taking up more memory space than I need?
Or giving the variables more suitable data types but costing myself 3 steps instead of just one?
Isn't there an efficient way to do arithmetic between two different data types in MIPS by using as few registers as possible?
.data
input: .double
cons1: .double 32
cons2: .word 5
cons3: .word 9
.text
#take double input from user
li $v0, 7
syscall #value is stored in f0 (and f1 probably)
#subtract first constant
ldc1 $f2, cons1 #load the value 32 into f2 and f3
sub.d $f12, $f0, $f2
#multiply by 5
lw $t1, cons2 #load 5 into t1
mtc1.d $t1, $f4 #move the value in t1 to f4
cvt.d.w $f4, $f4 #convert value to single precision to be able to do
#multiplication
mul.d $f12, $f12, $f4
#divide by 9
lw $t2, cons3 #load 9 into t2
mtc1.d $t2, $f6 #move the value in t2 to f5
cvt.d.w $f6, $f6 #convert value to single precision to be able to do
#division
div.d $f12, $f12, $f6
#show result
li $v0, 3
syscall

Related

Largest positive number in register

In assembly we can obtain negative numbers by substracting the positive one from FFFFh and then increasing it by one, so about half of registers is reserved for negative numbers.
And if we multiply two numbers, and the result is too large for one register, the rest is passed to dx.
So, my question is, what is the largest positive number, that can be stored in the register, because when I'm multiplying different pairs of numbers, neither ax nor dx is the same.
You can interpret the same register contents either as signed number, or as unsigned number. In the first case max value is 0x7F*** = 2^(regsize-1)-1 (32767 for 16 bit), in the second case max value is 0xFF** = 2^regsize-1 (65535 for 16 bit)
(Note that there are imul and mul commands for multiplication, the first accounts for signs).
Hex byte example
80 imul 2 = FF:00
80 mul 2 = 01:00

How to simply convert MIPS instruction to 32bits hex number

I'm working with ASM/MIPS technology and I want to train myself to conversion.
I wanna convert MIPS instructions to a 32bits hexadecimal number.
For example, I want to change sub$t0,$zero,$t1 to a hexa number.
I've find a lot of solutions, but everything is different.
Any idea ?
The MIPS sub instruction subtracts two registers and stores the result in a register.sub $d,$s,$t means $d = $s - $t
sub = function code 34 (see manual), 22 in hex, 100010 in bin (6 bits)
$t0 = 8 in decimal, 8 in hex, 01000 in bin (5 bits)
$zero = 0 in decimal, 0 in hex, 00000 in bin (5 bits)
$t1 = 9 in decimal, 9 in hex, 01001 in bin (5 bits)
So the machine code for your instruction is 01001 01000 00000 100010
For R-format instructions, the opcode, or "operation code" is always zero. rs, rt, and rd correspond to the two source and one destination registers.
The source sub$t0,$zero,$t1 therefore translates to 0x00094022 in hexadecimal notation.
Explanation
Every MIPS instruction is 32bits and translates to a 32bits number that can be written as machine code in hexadecimal format.
The instruction sub (subtract) has opcode 0x22. Therefore the rightmost digits has to be 22 (see above that they are).
sub is an instruction type R. This means the R instructions are used when all the data values used by the instruction are located in registers.
All R-type instructions have the following format:
OP rd, rs, rt
Where "OP" is the mnemonic for the particular instruction (in this case sub). rs, and rt are the source registers, and rd is the destination register. In this case, the sub instruction is used as:
sub $t0, $zero, $t1
In other words, your instruction means "subtract t1 from 0 and put the result in t0".
If you want to swap the values of two registers you can do it as follows:
.text
.globl __start
__start:
sw $t0, x
sw $t1, y
lw $t0, y
lw $t1, x
.data
x:
.word 0x000000FF
y:
.word 0xABCDE080
If you want to put the content in $t0 also in $t1, you can use the or instruction:
lui $t0, 0x0123
or $t1, $zero, $t0

How to do Division of two fixed point 64 bits variables in Synthesizable Verilog?

I'm implementing an Math equation in verilog, in a combinational scheme (assigns = ...) to the moment Synthesis tool (Quartus II) has been able to do add, sub and mul easly 32 bit unsigned absolute numbers by using the operators "+,- and *" respectively.
However, one of the final steps of the equation is to divide two 64 bits unsigned fixed point variables, the reason why is such of large 64 bit capacity is because I'm destinating 16 bits for integers and 48 bits for fractions (although, computer does everything in binary and doesn't care about fractions, I would be able to check the number to separate fraction from integer in the end).
Problem is that the operator "/" is useless since it auto-invokes a so-called "LPM_divide" library which output only gives me the integer, disregarding fractions, plus in a wrong position (the less significant bit).
For example:
b1000111010000001_000000000000000000000000000000000000000000000000 / b1000111010000001_000000000000000000000000000000000000000000000000
should be 1, it gives me
b0000000000000000_000000000000000000000000000000000000000000000001
So, how can I make this division for synthesizable verilog? What methods or algorithms should I follow, I'd like it to be faster, maybe a full combinational?
I'd like it to keep the 16 integers - 24 fractions user point of view. Thanks in advance.
First assume you multiply two fixed-point numbers.
Let's call them X and Y, first containing Xf fractional bits, and second Yf fractional bits accordingly.
If you multiply those numbers as integers, the LSB Xf+Yf bits of the integer result could be treated as fractional bits of resulting fixed-point number (and you still multiply them as integers).
Similarly, if you divide number of Sf fractional bits by number of Df fractional bits, the resulting integer could be treated as fixed-point number having Sf-Df fractional bits -- therefore your example with resulting integer 1.
Thus, if you need to get 48 fractional bits from your division of 16.48 number by another 16.48 number, append divident with another 48 zeroed fractional bits, then divide the resulting 64+48=112-bit number by another 64-bit number, treating both as integers (and using LPM_divide). The result's LSB 48 bits will then be what you need -- the resulting fixed-point number's 48 fractional bits.

bit-shift operation in accelerometer code

I'm programming my Arduino micro controller and I found some code for accepting accelerometer sensor data for later use. I can understand all but the following code. I'd like to have some intuition as to what is happening but after all my searching and reading I can't wrap my head around what is going on and truly understand.
I have taken a class in C++ and we did very little with bitwise operations or bit shifting or whatever you'd like to call it. Let me try to explain what I think I understand and you can correct me where it is needed.
So:
I think we are storing a value in x, pretty sure in fact.
It appears that the data in array "buff", slot number 1, is being set to the datatype of integer.
The value in slot 1 is being bit shifted 8 places to the left.(does this point to buff slot 0?)
This new value is being compared to the data in buff slot 0 and if either bits are true then the bit in the data stored in x will also be true so, 0 and 1 = 1, 0 and 0 = 0 and 1 and 0 = 1 in the end stored value.
The code does this for all three axis: x, y, z but I'm not sure why...I need help. I want full understanding before I progress.
//each axis reading comes in 10 bit resolution, ie 2 bytes.
// Least Significant Byte first!!
//thus we are converting both bytes in to one int
x = (((int)buff[1]) << 8) | buff[0];
y = (((int)buff[3]) << 8) | buff[2];
z = (((int)buff[5]) << 8) | buff[4];
This code is being used to convert the raw accelerometer data (in an array of 6 bytes) into three 10-bit integer values. As the comment says, the data is LSB first. That is:
buff[0] // least significant 8 bits of x data
buff[1] // most significant 2 bits of x data
buff[2] // least significant 8 bits of y data
buff[3] // most significant 2 bits of y data
buff[4] // least significant 8 bits of z data
buff[5] // most significant 2 bits of z data
It's using bitwise operators two put the two parts together into a single variable. The (int) typecasts are unnecessary and (IMHO) confusing. This simplified expression:
x = (buff[1] << 8) | buff[0];
Takes the data in buff[1], and shifts it left 8 bits, and then puts the 8 bits from buff[0] in the space so created. Let's label the 10 bits a through j for example's sake:
buff[0] = cdefghij
buff[1] = 000000ab
Then:
buff[1] << 8 = ab00000000
And:
buff[1] << 8 | buff[0] = abcdefghij
The value in slot 1 is being bit shifted 8 places to the left.(does this point to buff slot 0?)
Nah. Bitwise operators ain't pointer arithmetic, don't confuse the two. Shifting by N places to the left is (roughly) equivalent with multiplying by 2 to the Nth power (except some corner cases in C, but let's not talk about those yet).
This new value is being compared to the data in buff slot 0 and if either bits are true then the bit in the data stored in x will also be true
No. | is not the logical OR operator (that would be ||) but the bitwise OR one. All the code does is combining the two bytes in buff[0] and buff[1] into a single 2-byte integer, where buff[1] denotes the MSB of the number.
The device result is in 6 bytes and the bytes need to be rearranged into 3 integers (having values that can only take up 10 bits at most).
So the first two bytes look like this:
00: xxxx xxxx <- binary value
01: ???? ??xx
The ??? part isn't part of the result because the xxx part comprise the 10 bits. I guess the hardware is built in such a way that the ??? part is all zero bits.
To get this into a single integer variable, we need all 8 of the low bits plus the upper-order 2 bits, shifted left by 8 position so they don't interfere with the low order 8 bits. The logical OR (| - vertical bar) will join those two parts into a single integer that looks like this:
x: ???? ??xx xxxx xxxx <- binary value of a single 16 bit integer
Actually it doesn't matter how big the 'int' is (in bits) as the remaining bits (beyond that 16) will be zero in this case.
to expand and clarify the reply by Carl Norum.
The (int) typecast is required because the source is a byte. The bitshift is performed on the source datatype before the result is saved into X. Therefore it must be cast to at least 16 bits (an int) in order to bitshift 8 bits and retain all the data before the OR operation is executed and the result saved.
What the code is not telling you is if this should be an unsigned int or if there is a sign in the bit data. I'd expect -ve data is possible with an Accelerometer.

MIPS program to determine pass/fail for test grades

I'm writing a MiPS program that will examine a list of 15 test scores. And it is going to input from the terminal. The passing criterion is the score of 50. The outputs to the terminal will include the scores in each category and the number of students passing and failing. I should use input prompts and output statement. Please I need some help, just need some advice how to do it.
main:
li $t1,15 #load 15 into $t1
la $a1,array #load a pointer to array into $a1
I have a loop:
addi $t1,$t1,-1
li $v0,4
la $a0,prompt
syscall
I don´t want to give it away, so i´ll throw some guidelines.
You should read Assemblers, linkers and the Spim simulator. It´s a lot of help.
So here it goes.
Create two 15- word arrays.
.data
fail_vector: .word -1,-1,-1 ... #15 invalid words
passed_vector: .word -1,-1,-1 ... #15 invalid words
Load on some register the loop control variable.
li $t1,15
beq $t1,$zero,END
addiu $t1,$t1,-1
Now inside this loop read values
syscall... #SYS_READ
Then read this value (suppose you have it in register t4) and decide whether to store it in fail vector, or pass vector.
addiu t4,t4,-50 #subtract 50 from input value.
blez t4,FAILED #If its lower than 0, then read value is lower than 50 ->FAIL
PASSED:
#STORE VALUE INTO passed_vector
FAILED:
#STORE VALUE INTO failed_vector
When you are done with all the 15 values, print out the vectors. This is kind of tricky.
Before using your program, you should fill both vectors with some invalid value, like -1.
So when you are printing vector to screen, you should stop when you find one of this invalid values. And while you are at it, keep a counter to show how many passed / failed.
In pseudo-code
for both arrays
for (i in (0,15) and array[i] not -1)
print array[i]
add 1 to scores count //to count passed - failed test scores.
assembly (fill in the blanks)
END:
li $t4,15
li $t1,0
beq $t1,$t4,EXIT #condition. While ( i < 15) kind of thing.
addiu $t1,$t1,-1
#print out vectors and keep count on other registers
#then print them out.
syscall... #SYS_WRITE
EXIT: #exit syscall here.
Another tricky issue is the indexing of these vectors. Since they are arrays of words, then you should multiply by 4 (assuming 32 bit words) the loop control variable (classical i variable in C) to index the vector. If they were byte arrays, then no multiplication would be needed. And if they were short arrays...(well, you get my point)
For example:
passed_vector[i] #(C style sintax)
and let variable i be stored in register $t1
would turn out as:
sll $t2,$t1,2 #i * sizeof(word)
la $a0,passed_vector #$a0 points to passed_vector
add $a0,$a0,$t2 #$a0 now points to passed_vector + i
So now you could load/store to passed_vector[i]
sw $t3,0($a0) #0($a0) is passed_vector[0]
lw $t3,0($a0)
One way of solving these kind of things (that is, writing in assembly) is to write the program in C ( or some other language that you know ), and then translating it to assembly, instruction by instruction.
Ok, here's how to load both integer arrays (and only that)
.data
#These are two integer arrays. Each position is 32 bits long.
passed_vector: .word -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
failed_vector: .word -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
.text
#
# Previous code here.
#
li $t5,50 #For comparing test_scores against.
li $t0,0 # for (0..15)
li $t6,15 #
LOOP: beq $t0,$t6,CONTINUE # loops while i<15
li $v0,5
syscall
move $t1,$v0 #read test score and move it to register $t1
bge $t1,$t5,PASSED #if score >=50, load into passed_vector
FAILED: # else: test score lower than 50. Loads into failed vector
#dont forget to increment the failed counter here
sll $t2,$t0,2
sw $t1,failed_vector($t2)
addiu $t0,$t0,1 #i++
b LOOP
PASSED:
#dont forget to increment the passed counter here.
sll $t2,$t0,2
sw $t1,passed_vector($t2)
addiu $t0,$t0,1 #i++
b LOOP
CONTINUE: #other code

Resources