Mips questions on stack management, array coding, and overflow insight - arrays

How do I generate a function which takes elements from two vectors (while configuring stack properly) and passing the operands via stack to a FUNCTION? I defined my vectors in data portion of my code.
I don't know how to take the elements (1, 2, 3, 4, 5, 6) from the their vector and passing the operands via stack? Does anyone have any programming insight on this topic? I know an example of pushing a stack is
Sub sp, sp, 0
Li s0, 0x12345678
Sw s0, 0(sp)
And an example of popping a stack is
Lw s0, 0(sp)
Add sp, sp, 0
The part I'm confused at is implement my array elements while passing onto the stack. We were told specifically not to use registers to pass the parameters. Or else I would just itemize each number into a register and try and pass them that way. This is the second time that I am taking the class. I have the textbook. I have seen mips run. I have watched tutorials on mips via youtube. (#sys call is a term for just do it. #mad props to that guy) I guess what I am trying to say is that any helpful input would be appreciated.
I rewrote my code so its more readable for others.
.globl main #tells assembler that there is a global routine called main
.data
array1: .word 2, 4, 6, 8, 10, -12 #define array 1(6x1 vector)
array2: .word -1, 3, 5, 7, 9, 11 #define array 2(6x1 vector)
.text
main: # I am trying to configure the stack properly and pass parameters
# (vector elements) to dot_product function
addiu $sp, $sp, -32 # Allocating space on the stack.
# I need to pass two operands via stack onto num mult
# and multiply them together
sw $t1, array1+0x00($sp) #placing first element of array at Mem[Sp]
sw $t2, array1+0x04($sp) #placing second element of array at Mem[Sp+4]
sw $t3, array1+0x08($sp) #placing third element of array at Mem[Sp+8]
sw $t4, array1+0x0C($sp) #placing fourth element of array at Mem[Sp+12]
sw $t5, array1+0x10($sp) #placing fifth element of array at Mem[Sp+16]
sw $t6, array1+0x04($sp) #placing sixth element of array at Mem[Sp+20]
sw $ra, 24($sp) #saving return address
#Should I have an equivalent amount of lw so I
#de-allocate space on the stack? If so, then what
#is the purpose of allocating space on the stack
# in the first place. I don't see the point.
nop
jal num_mult #Call num_mult Function. I don't know why this is here. I just
# placed so others knew that I was inept at programming.
nop
dot_product: # takes elements from each vector and after configuring stack
# appropriately passes two operands via stack to num_mult
# calculate sum of RETURNED values from each call to num_mult
addi $t7, $v0, $v1 # $v0 and $v1 are return values from a function call
# This is my running sum calculator
mov $v0, $t7 # This returns the sum in $v0
addi $v1, $zero, 0 # Places 0 in $v1
bgt $t0, 32767, else # dot_product tests checks for overflow
addi $v0, $zero, 0 #if overflow, then dot_product will return zero in $v0
addi $v1, $zero, -1 #reg and -1 in $v1 reg
else:
addi $v0, 0, $t0 # if no overflow then dot_product will return product
addi $v1, 0, -1 # in $v0 reg and zero in $v1 reg
#dot_product should check for overflow in running sum calculation
#process. If overflow in running sum, dot_product should return zero
#too.
num_mult:
mult $a0, $a1 # takes two operands passed via stack and multiplies them
# together
mflo $t0
bgt $t0, 32767, else1 # num_mult tests result to determine if product can
# be accurately represented in 32 bits
addi $v0, $zero, 0 # if overflow, then num_mult will return zero in $v0
addi $v1, $zero, -1 # reg and -1 in $v1 reg
else1:
addi $v0, 0, $t0 # if no overflow then num_mult will return product in
addi $v1, 0, -1 # $v0 reg and zero in $v1 reg
end:
nop
b end #teacher asked for a 'spin' loop but this is all I have.
nop
The biggest problem that I have is understanding: taking elements from each vector and after configuring stack appropriately passes two operands via stack to num_mult & calculating sum of RETURNED values from each call to num_mult.
I will through it in the compiler tomorrow. I figure that I hang my dirty laundry up so others could eagerly make their comments. Thanks in advance

Related

I can’t figure out why my code is not displaying palindromes. Is is saying NO for everything when it should be saying Yes if it’s a 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
li $t8 0x00000000
####################################################################
# 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
jr $ra
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
nop
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, change_uppercase
change_uppercase:
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 $s4, $s2, $s3
beq $s4, $0, palindrome
nop
check_loop:
lw $s5, 0($s3)
lw $s6, 0($s2)
bne $s6, $t5, not_palindrome
nop
adjust_pointers:
addiu $s2, $s2, -4
addiu $s3, $s3, 4
slt $t8, $s3, $s2
bne $t8, $0, check_loop
nop
j palindrome
nop
palindrome:
li $a0, 1
call project3_print
move $s2, $s1
j main
nop
not_palindrome:
li $a0, 0
call project3_print
move $s2, $s1
j main
nop
I have tried working on debugging the Palindrome check and everything under that but it all appears fine. Im not sure if the problem remains in the poll_uart or not. this is the template I was given.
When I type in racecar it says no. when I type in gn. it says yes. when I type in night. it says no. So I am just confused.

UART Palindrome checker sending back "no" result all the time

I am working on a project which checks if a word is a palindrome and sends back "yes" or "no". For example the input "abba. data. abcd. reviver." should read "yes" "no" "no" "yes". Instead what I get is all "no". I have done some debugging and from what I can tell I am missing something in the palindrome_loop that is saying everything is not a palindrome. I am very new to all this so I am sorry if I sound uneducated in all this and kind of lost, but I am unsure of what else to do next any guidance or feedback is appreciated.
I am using PLPtool
Below is my code:
.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, $t2, $t1
beq $t3, $zero, 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
jr $ra
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_lowercase
nop
convert_lowercase:
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, $zero, palindrome
nop
palindrome_loop:
lw $s4, 0($s3)
lw $s5, 0($s2)
bne $s5, $t0, not_a_palindrome
nop
pointer_adjust:
addiu $s2, $s2, -4
addiu $s3, $s3, 4
slt $t8, $s3, $s2
bne $t8, $t0, palindrome_loop
nop
j palindrome
nop
palindrome:
li $a0, 1
call project3_print
move $s2, $s1
j main
nop
not_a_palindrome:
li $a0, 0
call project3_print
move $s2, $s1
j main
nop

C to MIPS - Instruction references undefined QTSpim

I was trying to do an assignment translating from C to MIPS but I get a instruction reference error in jal main. Here is what I have to translate:
void swap (int a, int b)
{
int temp=a;
a=b;
b=temp;
}
int distance (int a, int b)
{
if (b > a)
swap (a,b);
return (a-b)
}
And here is what I wrote:
.data #declare the variables
var1: .word 4, 7, 12, 5
var2: .word 15, 3, 6, 14
result: .space 4
.text
main:
la $t0, var1 #load address 'var1' into $t0
la $t1, var2 #load address 'var2' into $t1
la $t2, result #load address 'result' into $t2
li $t3, 0 # load imm (i=0)
for_loop:
bgt $t3, 4, for_done #when i>4 do not meet condition, exit
lw $t4, 0($t4) #result[i] = tmp
jal distance
addi $t3, $t3, 1 #i++
j for_loop
for_done:
la $t2, distance
ori $v0, $0, 4
syscall
distance:
blt $t0, $t1, exit
jal swap
sub $t5, $t0, $t1
jr $t5
swap:
lw $t6, 0($t0)
lw $t7, 0($t1)
sw $t6, 0($t1)
sw $t7, 0($t0)
exit:
I actually don't know what am I doing, just the basics of Assembly. I hope some of you could help me. :)
I'm sorry but your asm code had [at least] 15 bugs.
I've created two versions: one with the bugs annotated and a second with the bugs fixed and a working program
Note that due to the vagueness of the C code, I had to guess the true intent of the program and take [considerable] poetic license.
Here's the unchanged version with the bugs annotated [please pardon the gratuitous style cleanup]:
.data # declare the variables
var1: .word 4, 7, 12, 5
var2: .word 15, 3, 6, 14
# NOTE/BUG: this only reserves 4 bytes instead of the 16 we need to hold all
# four values
result: .space 4
.text
main:
la $t0,var1 # load address 'var1' into $t0
la $t1,var2 # load address 'var2' into $t1
la $t2,result # load address 'result' into $t2
li $t3,0 # load imm (i=0)
for_loop:
# NOTE/BUG: this goes one too far (i.e. we want i>=4)
bgt $t3,4,for_done # when i>4 do not meet condition, exit
# NOTE/BUG: $t4 is never intialized to anything, so this instruction will fault
# (e.g. equivalent to dererencing a null pointer in C)
lw $t4,0($t4) # result[i] = tmp
jal distance
# NOTE/BUG: the index variable 'i' is incremented, but distance does _not_ use
# (i.e.) distance will always use var1[0] and var2[0] on each iteration
addi $t3,$t3,1 # i++
j for_loop
# NOTE/BUG: what do we want to do here? -- print the result vector presumably
# NOTE/BUG: syscall 4 is to print a string -- it would require setting up $a0
# and _not_ $t2 but, even then, using 'distance' is wrong as distance is the
# function name and _not_ a string so we'd get garbage
# NOTE/BUG: we probably wouldn't even get that far because QtSpim would
# probably fault because distance is in the .text segment and not the .data
# segment
for_done:
la $t2,distance
ori $v0,$0,4
syscall
distance:
# NOTE/BUG: this is comparing _addresses_ instead of _values_ (i.e.) this
# compares (&var1[i] > &var2[i]) instead of var1[i] > var2[i])
# NOTE/BUG: this test is _reversed_, because this guarantees negative numbers
blt $t0,$t1,exit
# NOTE/BUG: jal is calling swap as a function, but swap is merely a label here
jal swap
# NOTE/BUG: based on the mips ABI, return values go into $v0
sub $t5,$t0,$t1
# NOTE/BUG: when 'jal distance' is called, the return address goes into $ra
# and to return to the place in main that called us, we want to do 'jr $ra'
# NOTE/BUG: this 'jr' should be at exit:
jr $t5
# NOTE/BUG: this actually swaps var1[i] and var2[i] -- would this be correct to
# modify the original arrays???
swap:
lw $t6,0($t0)
lw $t7,0($t1)
sw $t6,0($t1)
sw $t7,0($t0)
# NOTE/BUG: this is where the 'jr' should go
exit:
Here's the cleaned up and working version. I decided that it should store the distance in the result vector and then show all three vectors:
.data
# NOTE: lw/sw must be four byte aligned so keep these first
var1: .word 4, 7, 12, 5
var2: .word 15, 3, 6, 14
result: .space 16
msg_var1: .asciiz "var1:"
msg_var2: .asciiz "var2:"
msg_result: .asciiz "dist:"
msg_space: .asciiz " "
msg_nl: .asciiz "\n"
.text
main:
la $s0,var1 # load address of 'var1'
la $s1,var2 # load address of 'var2'
la $s2,result # load address of 'result'
li $s3,4 # number of elements in a given vector
li $s4,0 # load imm (i=0)
for_loop:
bge $s4,$s3,for_done # i <= count? if no, fly
jal distance
addi $s4,$s4,1 # i++
j for_loop
for_done:
la $a0,msg_var1
la $a1,var1
jal show
la $a0,msg_var2
la $a1,var2
jal show
la $a0,msg_result
la $a1,result
jal show
# exit program
li $v0,10
syscall
# distance -- calculate distance between two numbers in two vectors
#
# RETURNS:
# stores into 'result' vector
#
# global registers:
# s0 -- pointer to var1
# s1 -- pointer to var2
# s2 -- pointer to result
# s4 -- array index
#
# registers:
# t0 -- address and value of var1[i]
# t1 -- address and value of var2[i]
# t2 -- temp value
# t7 -- byte offset corresponding to index 'i'
distance:
sll $t7,$s4,2 # convert index to byte offset
addu $t0,$s0,$t7 # get &var1[i]
lw $t0,0($t0) # fetch var1[i]
addu $t1,$s1,$t7 # get &var2[i]
lw $t1,0($t1) # fetch var2[i]
bge $t0,$t1,distance_done # swap a/b to get abs val? if no, fly
# swap a/b
move $t2,$t0 # temp = a
move $t0,$t1 # a = b
move $t1,$t2 # b = temp
distance_done:
sub $v0,$t0,$t1 # get distance (i.e.) abs(a-b)
addu $t2,$s2,$t7 # get &result[i]
sw $v0,0($t2) # result[i] = distance
jr $ra # return
# show -- show vector
#
# arguments:
# a0 -- vector name
# a1 -- pointer to vector
#
# registers:
# t3 -- array remaining count
#
# clobbers:
# v0
show:
li $v0,4 # syscall to print string
syscall
move $t3,$s3 # get number of elements in vector
show_loop:
blez $t3,show_done # more to do? if no, fly
li $v0,4
la $a0,msg_space # output a space
syscall
# output vector[i]
li $v0,1 # syscall to output value
lw $a0,0($a1) # get vector value
syscall
addiu $a1,$a1,4 # advance pointer to next array element
addi $t3,$t3,-1 # bump down count
j show_loop
show_done:
# output newline
la $v0,4
la $a0,msg_nl
syscall
jr $ra # return

C ternary conditional operator to MIPS assembly with one side loaded from memory

C statement
A= A? C[0] : B;
Is is correct to write in assembly instruction this way?
Assuming $t1=A, $t2=B, $s1=base address of Array C:
beq $t1, $0, ELSE
lw $t1, 0($s1)
ELSE: add $t1, $t2, $0
No, it doesn't seem correct because add $t1, $t2, $0 will be executed even if $t1 != $0.
I hope that this works (not tested):
beq $t1, $0, ELSE
sll $0, $0, 0 # NOP : avoid the instruction after branch being executed
lw $t1, 0($s1)
j END
sll $0, $0, 0 # NOP : avoid the instruction after branch being executed
ELSE: add $t1, $t2, $0
END:
This code assumes that the elements of C are 4-byte long each.
You can avoid an unconditional j. Instead of structuring this as an if/else, always do the A=B (because copying a register is cheaper than jumping) and then optionally do the load.
On a MIPS with branch-delay slots, the delay slot actually helps us:
# $t1=A, $t2=B, $s1=base
beq $t1, $zero, noload
move $t1, $t2 # branch delay: runs always
lw $t1, 0($s1)
noload:
# A= A? C[0] : B;
On a MIPS without branch-delay slots (like MARS or SPIM in their default config):
# MIPS without branch-delay slots
# $t1=A, $t2=B, $s1=base
move $t3, $t1 # tmp=A
move $t1, $t2 # A=B
beq $t3, $zero, noload # test the original A
lw $t1, 0($s1)
noload:
# $t1 = A= A ? C[0] : B;
If we can clobber B and rearrange registers, we can save an insn without a branch-delay:
# MIPS without branch-delay slots
# $t1=A, $t2=B, $s1=base
beq $t1, $zero, noload
lw $t2, 0($s1)
noload:
# $t2 = A. B is "dead": we no longer have it in a register
A move $t3, $t2 before the BEQ could save B in another register before this sequence. Ending up with your variables in different registers can save instructions, but makes it harder to keep track of things. In a loop, you can get away with this if you're unrolling the loop because the 2nd copy of the code can re-shuffle to get registers back the way they need to be for the top of the loop.
move x, y is a pseudo-instruction for or x, y, $zero or ori x, y, 0. Or addu x, y, $zero. Implement it however you like or let your assembler do it for you.
Write a MIPS segment for the C statement
x=5; y=7;
Land(x,y,z) // z=x &y as procedure call
if (z>x) y=x+1

Mips 2d array function

I am attempting to create an algorithm that finds the trace of an n-by-n square matrix A.the trace of an n-by-n square matrix A is defined to be the sum of the elements on the main diagonal (the diagonal from the upper left to the lower right) of A.The main idea involved is that at this level multi-dimensional arrays are stored as one-dimensional arrays, and the multi-dimensional indexing (for a matrix with m rows and n columns) is converted to one-dimensional indexing.As I'am unfamiliar with mips attempts to integrate it into code are unsuccessful my latest attempt below.
I have set the registers to the following:
$a0 = base address of array (matrix), a
$a1 = n, number of rows and columns
$v0 = trace
$t0 = i
trace: move $v0, $zero
move $t0, $zero
bne $t0,$a1,end
sll $t1,$a0,4
add $t1,$t1,$t0
sll $t1,$t1,2
add $t2,$t1,$a0
lw $t0,0($t1)
addi $sp, $sp, 8
sw $t1,0($t0)
j trace
end: jr $ra
but to no avail the answer does not come out as desired the format of the algorithm should be as follows;
trace = 0
for i = 0 to n-1
trace = trace + a[i,i]
end for
I have added some comments indicating suspicious behaviour
trace: move $v0, $zero
move $t0, $zero
loop: bne $t0,$a1,end
sll $t1,$a0,4 ; t1 = a0 * 16, a0 is the base address, should probably be $t0
add $t1,$t1,$t0
sll $t1,$t1,2
add $t2,$t1,$a0
lw $t0,0($t1)
addi $sp, $sp, 8 ; What are you doing with $sp? perhaps this should be add $v0,$v0,$t0
sw $t1,0($t0) ; What are you storing here?
j trace ; This should probably jump to loop, or the code will never end
end: jr $ra
Also note that the sll assumes that the size of your matrix is 16.

Resources