Self-Modifying MIPS Code - mips

I'm trying to write a program in MIPS that continuously prompts for two integers and prints the sum until the sum is 0. The trick is that if the sum is 13, I need to call a method to change the assembled MIPS code so that
add $t2, $t0, $t1
becomes
and $t2, $t0, $t1
and all subsequent runs of the loop use the and instruction.
I have the summation loop working so that when 13 is the sum the method instMod is called which I want to modify the instruction. Unfortunately, I have no idea where to start and can't find any examples of this online. I assume I need to somehow get the hex code of the add out of the assembled code and replace it with the hex code for the and but I do not know how to do that or if that is the right course of action to take.
# Nick Gilbert
# MIPS Program to demonstrate self-modifying code
.data
num1Prompt: .asciiz "Enter num1: "
num2Prompt: .asciiz "Enter num2: "
num1: .word 0
num2: .word 0
addOut: .asciiz "ADD: "
andOut: .asciiz "AND: "
.text
main:
sumLoop:
la $a0, num1Prompt #Asking user for num1
li $v0, 4 #Call code to print string
syscall
li $v0, 5 #Call code to read an int
syscall
move $t0, $v0 #Moving read int to $t1
la $a0, num2Prompt #Asking user for num2
li $v0, 4 #Call code to print string
syscall
li $v0, 5 #Call code to read an int
syscall
move $t1, $v0 #Moving read int to $t2
add $t2, $t0, $t1 #Adding num1 and num2 together
la $a0, addOut
li $v0, 4
syscall
move $a0, $t2
li $v0, 1
syscall
beq $t2, 13, instMod #Calling method to modify add instruction if sum = 13
bne $t2, 0, sumLoop #If result is not yet 0, ask for new sum
endSumLoop:
li $v0, 10
syscall
instMod: #Method to change add instruction to an and instruction

Add a label at the instruction you want to replace, e.g:
instruction_to_be_replaced:
add $t2, $t0, $t1 #Adding num1 and num2 together
then in your routine instMod
instMod: #Method to change add instruction to an and instruction
lw $t1, instruction_to_replace
sw $t1, instruction_to_be_replaced
j sumLoop # go back to your sumLooop
instruction_to_replace:
and $t2, $t0, $t1
The code loads in temporary register $t1 the contents of the instruction you want to replace, and then stores it in the location labelled instruction_to_be_replaced.
The "source" of the instruction goes labelled in instruction_to_replace.
To do this, you need to be able to write on the code section which I assume you have otherwise you would not be asking this question.

Try this:
Assemble the instruction that you need to an object file
Extract the hexadecimal of the equivalent machine code
Place a label in front of the code you need to change
mov the hexidecimal from step 2 into the location from step 3 in your instMod section
For this to function the two instructions with operands must be of identical length. If they are not, pad the original or the replacement with nop as appropriate.

Related

How to count the number of words and print the result in mips?

So I am fairly new to mips and am trying to do a hmwk.
The exercise is to create a block of words with 0 as the last word , to count them knowing we don't consider the last element 0 and print out the result.
Here is my code for the moment
.data
blockOfWords: .word 12,43,549,7,60,0
.text
la $a0, blockOfWords #putting address of blockOfWords in $a0
loopStart: sll $t1, $s3,2 #iterates thru blockOfWords by jumping by 4 bytes, $t1=4*i
add $t1,$t1,$a0 #we increment the address by four so as to advance in the array after every iteration
lw $a1, 0($t1) #$a1=blockOfWOrds[i]
beq $a1,$zero, Exit #if the value in $a1[i]==0, exit the loop
addi $s0,$s0,1 #else, increment by one the cnt=$s0
j loopStart #and continue looping thru blockOfWords
Exit:
#how do i print the result?
So instead of doing a shift left logical it is a lot easier to up the memory address in the array
In this way the lw $a1, 0($a0) is just grabbing the beginning of the word
it is then incremented by 4 after a check for the 0. This makes the function much simpler and easier to read.
.data
blockOfWords: .word 12,43,549,7,60,0
.text
main:
la $a0, blockOfWords # Load beginning address into $a0
loopStart:
lw $a1, 0($a0) # load the value at the beginning of $a0 into $a1
beq $a1,$zero, exit # Check if $a1 is 0, if so jump to exit
addi $s0,$s0,1 # Add one to the count
addi $a0, $a0, 4 # Up the intial index of the array by 4, up one word
j loopStart # Re-loop
exit:
li $v0, 1 # Load the value of 1 into the $v0 register
la $a0, ($s0) # Load the counter into the $a0 address
syscall
li $v0, 10 # A value 10 syscall to indicate end of program
syscall

Swapping every two characters in a string and fixing exception error

I'm getting an exception error and I'm not sure why. I need some help with the fix for the exception and how to swap chars in the user inputted string?
The input and output should look something like this:
Input : Apples
Output : pAlpse
.text
main:
#Prompt user for string
la $a0, promptStr
li $v0, 4
syscall
#Get String
li $v0,8
la $a0, buffer
li $a1, 20
syscall
move $t0, $a0
#Initialize pointer to start of string (specify register)
la $t0, buffer
#Get a char from the string (register)
loop: lb $t1, ($t0)
I'm getting Instruction references undefined here and I have no idea why because I've used it in similar problems to branch out of a loop when at the end of a string.
Here's the error message
Instruction references undefined symbol at 0x00400058
[0x00400058] 0x11200000 beq $9, $0, 0 [strEnd-0x00400058]; 48: beqz $t1, strEnd
#IF at end of the string branch to endStr
beqz $t0, strEnd
#Add 1 to the string pointer
add $t0, 1#Fixed
#get the next char in the string
lb $t2, ($t0)
I don't know if this code is right. I just need help understanding
how to swap chars and what the code will look like.
#Swap the 2 chars by writing them back to the original string
sb $t2, ($t0)
sb $t1, ($t0)
#Add 1 to the string pointer
add $t0, 1 #Fixed
#Jump back to loopStart
j loop
#Display modified string
endStr: la $a0,ans
li $v0, 4
syscall
move $a0,$t2
li $v0, 4
syscall
#Blankline
la $a0, end
li $v0, 4
syscall
#Exit porgram
li $v0, 10
syscall
.data
buffer: .space 20
promptStr: .asciiz "Input a string : "
blankLine : .asciiz "\n"
The issue with your error beqz $t0, strEnd is a typo: the label you meant is endStr. The error itself tells you this, highlighting [strEnd-0x00400058] as undefined. Additionally, spim warns me on load:
The following symbols are undefined:
end
ans
After fixing the missing labels, the logic is not quite correct, although it looks like you're on the right track. The idea is to step over the string in character pairs, swapping elements and exiting when hitting a null character (and, optionally, a newline depending on how you want to handle that--stripping/chomping it is probably best).
For starters and a rather minor point, there is a dead instruction near the top of your program:
move $t0, $a0 # $t0 will be overwritten by the next la
#Initialize pointer to start of string (specify register)
la $t0, buffer
Stepping into the loop and the main logic, the code beqz $t0, strEnd uses the address of the string which will never be 0 as the branch condition rather than the byte at that address, $t1. This gives an infinite loop.
Next, there is a problem with $t0, the pointer that walks the string. The logic
sb $t2, ($t0)
sb $t1, ($t0)
doesn't work because $t0 was already incremented so the code loses track of the previous byte address after
#Add 1 to the string pointer
add $t0, 1#Fixed
A solution is to store $t0 in a temporary register before any add $0, 1 operations. Something like:
move $t3 $t0 # save the address of buf for swap
# ... later on, after incrementing `$t0` ...
sb $t2, ($t3)
sb $t1, ($t0)
You could also use an indexing strategy here by adding/subtracting offsets or using an index to walk the string.
Lastly, I'm not sure what move $a0,$t2 should do towards the end of the program when you're printing.
Here's one possible solution that addresses these issues and generally cleans up the logic:
.text
main:
# prompt user for string
la $a0 prompt
li $v0 4
syscall
# get string
la $a0 buffer
li $a1 20
li $v0 8
syscall
move $s0 $a0 # incrementable pointer to buf
loop:
# t1 = *(buf++) and exit if '\0' or '\n'
move $t0 $s0 # save the address of buf for swap
lb $t1 ($t0) # t1 = *buf
beqz $t1 end # break if '\0'
beq $t1 10 end # break if '\n'
add $s0 1 # buf++
# t2 = *buf and exit if '\0' or '\n'
lb $t2 ($s0) # t2 = *buf
beqz $t2 end # break if '\0'
beq $t2 10 end # break if '\n'
# swap the chars
sb $t2 ($t0) # *prev_buf = curr_buff_char
sb $t1 ($s0) # *buf = prev_buff_char
# increment pointer and continue to the next pair
add $s0 1 # buf++
j loop
end:
# display modified string
la $a0 buffer
li $v0 4
syscall
# exit program
li $v0 10
syscall
.data
buffer: .space 20
prompt: .asciiz "Input a string : "
Sample runs:
$ spim -f swap_alternating_chars.s
Input a string : Apple
pAlpe
$ spim -f swap_alternating_chars.s
Input a string : Apples
pAlpse

Can anyone tell what mistake i'm doing in this MIPS code?

.data
array: .space 10
prompt: .asciiz "Enter an integer (0 to quit) :"
text: .asciiz "After sorting, the list of integers is:"
.text
.globl main
main:
la $a1, array
read_numbers:
# Rest of code omitted for brevity...
beqz $v0, sort
j read_numbers
sort:
la $a1, $array
li $v0, 4
la $a0, text
syscall
loop:
lw $t0, 0($a1)
addiu $a1, $a1, 4
beqz $t0, done
li $v0, 1
move $a0, $t0
syscall
j loop
Assuming that the code above is formatted correctly in the file rather than all on one line, and ignoring that you are missing code at
# Rest of code omitted for brevity
And ignoring that you haven't said what it is supposed to do, or what it does do ….
The first thing I see if that you are branching in main:
read_numbers: # Rest of code omitted for brevity...
beqz $v0, sort
j read_numbers
But since you didn't set v0 to anything, it is set to whatever value it was set to before your code (and when I go to run it, non zero)
So, it never branched to the sort routine, and jumps to read_numbers and does an endless loop.
If it was in the 'sort', it will grab the first number from array, which is 0 (unless you had populated the array somehow), see that it was 0 and attempt to branch to 'done' which also isn't in your code.

MIPS Output Error

I have an unknown output error after entering 2nd value which is after entering base number.
Hopefully,some of you could identify my error:
ERROR:Instruction references undefined symbol at 0x00400060
[0x00400060] 0x102a0000 beq $1, $10, 0 [hex-0x0040005c]
PROGRESS:Currently stucked at Step 2.
What i want to do is,
1)User enter a decimal value
2)User enter type of conversion
3)Go to desired subroutine depending on type of conversion chosen earlier
4)Display output
.data
prompt: .asciiz "Enter the decimal number to convert: "
base: .asciiz "Select type of base (2 for binary,16 for hexadecimal or 8 for octal): "
ans1: .asciiz "\nBinary Output is equivalent:"
ans2: .asciiz "\nOctal Output is equivalent:"
ans3: .asciiz "\nHexadecimal Output equivalent:0x"
result1: .space 8
.text
.globl main
main:
la $a0, prompt #Display message
li $v0, 4
syscall
li $v0, 5
syscall
beq $v0, $zero, Exit #Exit if 0 decimal is entered
move $t0, $v0 #Else copy value entered into temporaries
askbase:
li $v0, 4
la $a0, base #Display message
syscall
li $v0, 5
syscall
add $t1,$zero,$v0 #Add desired value/base entered into t1
beq $t2,16,hex #if base 16 is entered,goto hex subroutine
beq $t2,8,oct
beq $t2,2,bin
la $a0, ans3
li $v0, 4
syscall
li $t0, 8 # counter
la $t3, result1 # where answer will be stored
Hex:
beqz $t0, Exit # branch to exit if counter is equal to zero
rol $t2, $t2, 4 # rotate 4 bits to the left
and $t4, $t2, 0xf # mask with 1111
ble $t4, 9, Sum # if less than or equal to nine, branch to sum
addi $t4, $t4, 55 # if greater than nine, add 55
b End
Sum:
addi $t4, $t4, 48 # add 48 to result
End:
sb $t4, 0($t3) # store hex digit into result
addi $t3, $t3, 1 # increment address counter
addi $t0, $t0, -1 # decrement loop counter
j Loop
Exit: la $a0, result1
li $v0, 4
syscall
la $v0, 10
syscall
It looks like you have a typo at line 29 beq $t2,16,hex should be beq $t2,16,Hex. Note the capital letter on Hex. You also have a number of undefined labels: Loop, oct, bin... Without those labels you are going to have issues. It is a good idea to just set some up as place holders (until you have their subroutines defined). Maybe have them all branch to Exit:. The assembler cannot resolve your branch instructions without having the actual labels to go to.
You haven't stored anything in $t7, so there's no particular reason to expect that $t7 would equal 16.
What you probably wanted to write is:
beq $t1,16,hex
Since $t1 is the register that you stored the base in.
However, I really don't see why you would want that jump with the way the code currently is structured. The hex subroutine relies on a few registers (like $t0 and $t3) to have been initialized to certain values, and that initialization would be skipped if that beq is taken.

displaying the factorial of each value of the index of a loop does not work. MIPS

I am trying to display the factorial of the current value of the index of a loop in MIPS but, my loop does not work at all. It keeps freezing every time I run the code. Let say I have a for loop such that (i=1; i<=5; i++), for each value of i, I would like to display the current factorial(i). The number 5, is actually provided by the user, in other words, the number can change(1-10). I have tried to figure out why my loop causes the code to freeze, but I have no clues so far. Your help will be very appreciated. The code is bellow.
.data # data declaration section; specifies values to be stored
# in memory and labels whereby the values are accessed
Prompt: .asciiz "\nEnter the number to compute the factorial:\n"
message: .asciiz "\nResult of computation is:\n"
result: .word 0
#--------------------------------
# main function |
#--------------------------------
.text # Start of code section
.globl main
main: # Execution begins at label "main". The prompt is displayed.
li $v0, 4 # system call code for printing string = 4
la $a0, Prompt # load address of string to be printed into $a0
syscall # call operating system to perform operation;
# $v0 specifies the system function called;
# syscall takes $v0 (and opt arguments)
# Read integer N
li $v0, 5
syscall
move $t0, $v0 #copy integer N into $t0
li $t1, 1 #initialize i=1
loop:
blt $t0, $t1, exit_loop # if number<1, exit...
move $a0, $t0 # copy N into $ao
jal fact #else call fact
sw $v0, result
li $v0, 4 #Display message
la $a0, message
syscall
li $v0, 1 #print result
lw $a0, result
syscall
sub $t0, $t0, 1 #Decrement N by one, N--
j loop
exit_loop:
jr $ra #return address
exit: #exit the program
li $v0, 10
syscall
.globl fact
.ent fact
fact:
subu $sp, $sp, 8
sw $ra, ($sp)
sw $s0, 4($sp)
li $v0, 1 #check base case
beq $a0, 0, end_fact
move $s0, $a0 #fact(n-1)
sub $a0, $a0, 1
jal fact
mul $v0, $s0, $v0 #n*fact(n-1)
end_fact:
lw $ra ($sp)
lw $s0, 4($sp)
addu $sp,$sp, 8
jr $ra
#end of factorial function
Your problem is in the instruction at exit_loop label:
exit_loop:
jr $ra #return address
You are jumping again into the loop because you haven't saved $ra at the start of your main function and you are also not restoring it before issuing that jr
In fact, your code, as is, should just terminate (syscall 10) and not issue a jump because you are implementing the main function and not a function to be called from elsewhere.
So, i'd change that code to:
exit_loop:
li $v0, 10
syscall
Looking a bit more into your code, you already have that code (on label exit), so you might just remove your exit_loop code and branch to exit instead of exit_loop in the branch that goes just after loop label.