I am writing this code for a problem where I have to read integers from file and store them into an array to perform other operations. So far I have been able to read from file and store them into the buffer.
#### Read Data from File
li $v0, 14 # system call for read from file
move $a0, $s6
la $a1, buffer # address of buffer from which to read
li $a2, 1000 # hardcoded buffer length
syscall # read from file
When I do this: it sucessfuly displays the content of the file which are integers on separate lines.
li $v0, 4 # syscall for printing a string
la $a0, buffer # load read data in $a0
syscall
I am stuck at this point where I have to store these integers in the buffer into an array. How is this done?
You didn't give us a lot of information to go on so I will be assuming your file looks something like this:
1234 523 54326 7131
(It can be line-delimited, the concept is the same)
Once you read the number into a string, you have to parse it into an integer. And then store it in an integer array. Your array should look something like this:
.align 2 # word-aligned
array: .space 40 # a word array of 10 elements
To parse the string to an integer you can barrow the concept from C, which has atoi() which looks something like this:
#
# int atoi ( const char *str );
#
# Parse the cstring str into an integral value
#
atoi:
or $v0, $zero, $zero # num = 0
or $t1, $zero, $zero # isNegative = false
lb $t0, 0($a0)
bne $t0, '+', .isp # consume a positive symbol
addi $a0, $a0, 1
.isp:
lb $t0, 0($a0)
bne $t0, '-', .num
addi $t1, $zero, 1 # isNegative = true
addi $a0, $a0, 1
.num:
lb $t0, 0($a0)
slti $t2, $t0, 58 # *str <= '9'
slti $t3, $t0, '0' # *str < '0'
beq $t2, $zero, .done
bne $t3, $zero, .done
sll $t2, $v0, 1
sll $v0, $v0, 3
add $v0, $v0, $t2 # num *= 10, using: num = (num << 3) + (num << 1)
addi $t0, $t0, -48
add $v0, $v0, $t0 # num += (*str - '0')
addi $a0, $a0, 1 # ++num
j .num
.done:
beq $t1, $zero, .out # if (isNegative) num = -num
sub $v0, $zero, $v0
.out:
jr $ra # return
(Just like the C atoi() function, it's got no error-checking mechanism, you might want to add that).
Parse each individual number from the file (by passing it to atoi()) and store it in the array as an integer.
Related
I'm new to MIPS and having some trouble with my program.
Right now my code works at inserting an integer value into the array at a specified index. But currently, when the integer is inserted it replaces the value that was previously there. How do I instead shift all elements after said index so the array's length increases by 1 and no values are lost?
Note that I'm using a special integer -22 to indicate the end of my array.
Any help would be greatly appreciated! Thank you!
Below is my code:
.data
beginarray: .word 1, 2, 3, 4, -22
array: .space 4000
str_command: .asciiz
str1: .asciiz "\nEnter an Integer: "
str2: .asciiz "\nAt what index: "
.text
.globl main
main:
# get length
la $a0, beginarray
jal length
addi $s0, $v0, 0 # $s0 = returned length
# copy beginarray into array
la $a0, beginarray
la $a1, array
jal copyarray
jal insert
# print array
la $a0, array
jal printarray
li $v0,10
syscall
# takes pointer to array as input
length: addi $t0, $zero, -22 # $t0 = -22
addi $v0, $zero, 0 # $v0 = 0 (length)
add $t1, $a0, $zero # $t1 = pointer to first int
calclength: lw $t2, 0($t1) # $t1 = value of current int
beq $t2, $t0, returnlength # if $t1 = -22
addi $v0, $v0, 1 # increment length
addi $t1, $t1, 4 # move pointer to next int
j calclength # loop
returnlength: jr $ra
# takes two pointers as input (pointer to first array and pointer to second array)
# copies contents of first array into second array
copyarray: addi $t0, $a0, 0 # $t0 = pointer to array1
# addi $t1, $a1, 0 # $t1 = pointer to array2
addi $t4, $zero, -22 # $t4 = -22
calccopy: lw $t2, 0($t0) # $t2 = value of array1
#lw $t3, 0($t1) # $t3 = value of array2
sw $t2, 0($a1) # store int in array
beq $t2, $t4, returncopy # if $t2 = -22
addi $t0, $t0, 4 # move a1 pointer to next int
addi $a1, $a1, 4 # move a2 pointer to next int
j calccopy
returncopy: jr $ra
# print array
printarray: add $t1, $a0, $zero # $t1 = pointer to first int
addi $t0, $zero, 0 # set counter to zero
printloop: beq $t0, $s0, done
# load word from addrs and goes to the next addrs
lw $t2, 0($t1)
addi $t1, $t1, 4
# syscall to print value
li $v0, 1
move $a0, $t2
syscall
# syscall for printing space
li $a0, 32
li $v0, 11
syscall
#increment counter
addi $t0, $t0, 1
j printloop
done: jr $ra
insert:
li $v0, 4 # system call code for print_str
la $a0, str1
syscall
li $v0 5 # system call code for read_int
syscall
move $t3, $v0 # $t3 = integer entered
li $v0, 4 # system call code for print_str
la $a0, str2
syscall
li $v0 5 # system call code for read_int
syscall
move $t1, $v0 # $t1 = index entered
li $t0, 0 # $t0 = counter
la $t2, array # $t2 = base address of array
for: bge $t0, $t1, end_for
add $t2, $t2, 4 # get address of next array element
add $t0, $t0, 1 # increment loop induction variable
b for
end_for:sw $t3, ($t2)
jr $ra
I am attempting to recreate a C-snippet in assembly. The C program essentially:
creates a char array[256] along with a pointer *A initialized to NULL.
User input of some string is then stored into the char array
while each ith element of the array != 0
check it the ith element is == some DEFINED char value V.
If array[i] == V -> set A = &array[i]
break
Lastly check if A is still initialized to NULL
if not pint address and value of A
else not found
Unfortunately, when I run the MIPS code with Qtspim I get the aforementioned error Exception occurred at PC=0x004000a8
-> (Abort after)
-> Followed by Unaligned address in inst/data fetch: 0x1001012b
I feel like it has something to do with the way that I store my byte values in the array or the way I am accessing those memory locations but I can't figure out what the problem is. Any insight that anyone can offer would be greatly appreciated. Happy holidays everyone!
Bellow is my assembly code:
# global functions aforementioned error
.globl main
# .text assembler directive
.text
# main
main:
# register map
# use $t0 for i
# use $t1 for NULL
# use $s0 for &inputArray[i]
# use $s1 for indexed array value
# use $s2 for the base address inputArray
# use $s3 for result
# use $t2 for constCharlwr
# Prompt user for input
la $a0, str1
li $v0, 4
syscall
# Get user input and store in input array
la $a0, inputArray
li $a1, 256
li $v0, 8
syscall
# set up registers
lb $t0, i
lb $t1, NULL
la $s2, inputArray
lb $s3, result
lb $t2, constCharlwr
li $s0, 0
add $s0, $s0, $s2 # s0 = &arrayA[i]
while: # while loop
add $s0, $s0, $t0 # base address + 1 byte
lb $s1, 0($s0) # load array index value into s1
beq $s1, $t1, outsideWhile # inputArray[i] != '\0'
if1:
bne $s1, $t2, outsideIf # check if inputArray[i] == e
move $s3, $s1 # if true copy s1(inputArray[i] into s3(result)
sb $s3, result
j outsideWhile
outsideIf:
addi $t0, $t0, 1
j while
outsideWhile:
if2:
beq $s3, $t1, else
# Print results
# Print string 2
lw $a0, str2
li $v0, 4
syscall
# Print address of result
la $a0, result
li $v0, 4
syscall
# Print next line
lb $a0, nextline
li $v0, 4
syscall
# Print string 3
lw $a0, str3
li $v0, 4
syscall
# Print result char value
lb $a0, result
li $v0, 4
syscall
# Print next line
lb $a0, nextline
li $v0, 4
syscall
j exitPrgm
else:
# Print No match
lw $a0, str4
li $v0, 4
syscall
# Exit the program by means of syscall.
exitPrgm:
li $v0, 10 # Sets $v0 to "10" to select exit syscall
syscall # Exit
# .data assembler directive
.data
inputArray: .space 256
constCharlwr: .byte 'e'
result: .byte 0
NULL: .byte 0
i: .byte 0
str1: .asciiz "Enter a word to search for letter e: \n"
str2: .asciiz "First match at address "
str3: .asciiz "The matching character is "
str4: .asciiz "No match found\n"
nextline: .asciiz "\n"
Ok, so I figured out my initial problem, I was not using la for my sys calls and once I fixed that I started getting the output that I wanted. The only problem now is I can figure out how to print the actual address of the result. Can someone help me with this.
New Code, Trying a few diffrent things to get the memory address but nothing seems to work.
# Prompt user for input
la $a0, str1
li $v0, 4
syscall
# Get user input and store in input array
la $a0, inputArray
li $a1, 255
li $v0, 8
syscall
# set up registers
lb $t0, i
lb $t1, NULL
la $s2, inputArray
lb $s3, result
lb $t2, constCharlwr
li $s0, 0
add $s0, $s0, $s2 # s0 = &arrayA[i]
while: # while loop
add $s0, $s0, $t0 # base address + 1 byte
lb $s1, 0($s0) # load array index value into s1
beq $s1, $t1, outsideWhile # inputArray[i] != '\0'
if1:
bne $s1, $t2, outsideIf # check if inputArray[i] == e
sb $s1, result
j outsideWhile
outsideIf:
addi $t0, $t0, 1
j while
outsideWhile:
if2:
beq $s1, $t1, else
# Print results
# Print string 2
la $a0, str2
li $v0, 4
syscall
# Print address of result
la $t9, result
li $t8, 4
li $t0, 0
#for:
# bge $t0, $t8 outsideFor
# add $t9, $t9, $t0
# lb $t7, 0($t9)
# sb $t7, resultAddr
# la $a0, resultAddr
# li $v0, 4
# syscall
# addi $t0, $t0, 1
# j for
#outsideFor:
sb 0($t9) resultAddr
la $a0, resultAddr
li $v0, 4
syscall
sb 1($t9) resultAddr
la $a0, resultAddr
li $v0, 4
syscall
sb 2($t9) resultAddr
la $a0, resultAddr
li $v0, 4
syscall
sb 3($t9) resultAddr
la $a0, resultAddr
li $v0, 4
syscall
# Print next line
la $a0, nextline
li $v0, 4
syscall
# Print string 3
la $a0, str3
li $v0, 4
syscall
# Print result char value
la $a0, result
li $v0, 4
syscall
# Print next line
la $a0, nextline
li $v0, 4
syscall
j exitPrgm
else:
# Print No match
la $a0, str4
li $v0, 4
syscall
I am writing a program using MIPs that takes an input string from the user and outputs the longest substring of consecutive identical characters. My problem seems to be that 'end' seems to be reached before the program outputs the target string. Any help is appreciated!
main:
la $a0,input_msg # Prompting input from user
li $v0,4
syscall
li $v0, 8 # Reading string and storing in memory
la $a0, input_string
li $a1, 100
syscall
li $t0, 0
la $t1, input_string # address of the first element
lb $a0, ($t1) # input_string[0] (the first character)
move $s0, $a0 # current_char=read_char();
move $s1, $a0 # previous_char = input_string[0];
move $s2, $a0 # final_char=input_string[0];
li $s3, 1 # current_num_chars=1;
li $s4, 1 # final_num_chars=1;
la $t5, new_line
loop:
addi $t0, $t0, 1 # for(k=1; k<i-1; k++) {
add $t1, $t1, $t0
lb $t3, ($t1)
beq $s5, $t3, print_final
move $s0, $t3 # current_char=input_string[k];
bne $s0, $s1, else # if(current_char == previous_char)
addi $s3, $s3, 1 # current_num_chars++;
bge $s3, $s4, increase_final # if(current_num_chars >= final_num_chars) {
move $s1, $s0
j loop # else do nothing
increase_final:
move $s2, $s0 # final_char=current_char;
move $s4, $s3 # final_num_chars=current_num_chars;
move $s1, $s0
j loop # }
else:
move $s1, $s0
j loop # else {
# current_num_chars=1;
# }
# }
print_final:
la $a0, output_msg
li $v0, 4
syscall
li $t2, 0
print_chars:
li $t2, 0 # for(k=0; k<final_num_chars; k++) {
beq $t2, $s3, end
move $a0, $s2 # print_char(final_char);
li $v0, 11
syscall
j print_chars # }
end:
li $v0, 4 # print_string("\n");
la $a0, new_line
syscall
li $v0, 10 # exit()
syscall
Your print_chars loop compares $t2 (which you just set to 0) to $s3 to decide whether or not to jump to end. Thus, it will always make the same comparison, and your loop will either exit the first time it is encountered, or never end.
Plus, the comment suggests that you should be comparing to $s4, not $s3.
again
I'm writing a MIPS program that reads 5 integers and stores them in array. It then creates a new array whose values are the values of the initial array multiplied with their index. After that I need to find the max and min of the second array.
I'm new to MIPS, here is the code I wrote :
.data
Prompt: .asciiz "\n Enter 5 Integers :" #gets number of integers
op: .asciiz "\n Enter Option: \n 1-Find the mult \n 2- Find the max \n 3-Find the min \n 4-Exit"
invalidm: .asciiz "\n Bad Input:"
vec: .space 20
vec2: .space 20
.text
.globl __start
__start:
la $a0,Prompt
li $v0,4
syscall
#Reading integers and store theme in the array
options:
la $a0,op
li $v0,4
syscall
li $v0,5
syscall
blt $v0,1,invalid#
bgt $v0,4,invalid#
beq $v0, 1, multp#
beq $v0, 2, max#
beq $v0, 3, min#
beq $v0, 4, exitpro#
j options
multp:
#multiply every element in the array with its index and store them in the new array vec2
j options
max:
#find the max of the array vec
j options
min:
#find the min of the array vec
j options
invalid:
la $a0,invalidm
li $v0,4
syscall
j options
exitpro:
li $v0,10
syscall
I'd like to make a quick note on coding style: assembly is a bit diabolical. It's very tempting to say "oh, I do these 5 lines a lot, I should just jal here and reuse it". This makes sense at first, but tends to result in confusing spaghetti code that can't go 3 lines without jumping somewhere. As such, my code has a bit of code repetition, but nothing too bad.
I altered your initial conditions a little, the length of the array is hard coded, but it uses a stack-allocated array rather than one that's statically allocated at initialization in the .data header. Instead the array's length is allocated in the data section. This was just personal preference. It has the benefit that the code should work for any length > 0.
.data
PromptHead: .asciiz "\n Enter "
PromptTail: .asciiz " integers\n"
Minstr: .asciiz "Min: "
Maxstr: .asciiz "Max: "
newline: .asciiz "\n"
inputs: .word 5
.text
main:
# Output prompt for input
## "\n Enter "
la $a0, PromptHead
li $v0, 4
syscall
## "5"
lw $a0, inputs
li $v0, 1
syscall
## " integers\n"
la $a0, PromptTail
li $v0, 4
syscall
# Backup sp value before allocating array
move $fp, $sp
# calculate size of vector (numinputs * sizeof(int) = numinputs * 4 = numinputs << 2), store in $s1
lw $s0, inputs
sll $s1, $s0, 2
# dynamically grow stack to include array of ints
add $sp, $sp, $s1
# Loop initialization
# i = 0
move $s1, $zero
# while i < numInputs
ReadInput:
slt $s2, $s1, $s0
beq $s2, $zero, ReadInputDone
# Read integer
li $v0, 5
syscall
# Calculate array offset for this loop
sll $s2, $s1, 2
add $s2, $fp, $s2
# Store array value at calculated address
sw $v0, 0($s2)
# i++
addi $s1, $s1, 1
j ReadInput
ReadInputDone:
# Scale(array, length)
move $a0, $fp
move $a1, $s1
jal Scale
# max = FindMax(array, length)
jal FindMax
# Back up return val, print boilerplate
move $t0, $v0
li $v0, 4
la $a0, Maxstr
syscall
move $a0, $t0
li $v0, 1
syscall
la $a0, newline
li $v0, 4
syscall
# min = FindMin(array,length)
move $a0, $fp
jal FindMin
# Back up return val, print boilerplate
move $t0, $v0
li $v0, 4
la $a0, Minstr
syscall
move $a0, $t0
li $v0, 1
syscall
la $a0, newline
li $v0, 4
syscall
# Exit program
li $v0, 10
syscall
# Scale(array, length)
# Scales each array element by index+1. This does not alter any s or a registers,
# Alters the array in place.
Scale:
# Backup return address and fp on stack.
# Not strictly necessary here, but usually good to do this by habit
# when you're learning
sw $fp, 0($sp)
sw $ra, 4($sp)
addi $fp, $sp, 8
move $sp, $fp
# Load arguments into scratch registers
move $t0, $a0
move $t1, $a1
# Loop initialization as above
# i = 0
move $t2, $zero
# while i < 5
ScaleInput:
slt $t3, $t2, $t1
beq $t3, $zero, ScaleInputDone
# Calculate element offset, store address in $t3
sll $t3, $t2, 2
add $t3, $t0, $t3
# Load array element at $t3, multiply it by current index+1
# Grab it from the multiplication register (assume no overflow)
# then store the result back in the array
lw $t5, 0($t3)
addi $t4, $t2, 1
mult $t5, $t4
mflo $t5
sw $t5, 0($t3)
# i++
addi $t2, $t2, 1
j ScaleInput
ScaleInputDone:
# Unwind stack, restore frame pointer and
# return address. Again, not necessary here, but good
# practice
lw $ra, -4($fp)
move $sp, $fp
lw $fp, -8($fp)
jr $ra
# int Max(array, length); result returned in $v0,
# No s or a registers are altered
FindMax:
# Backup return address and fp on stack.
sw $fp, 0($sp)
sw $ra, 4($sp)
addi $fp, $sp, 8
move $sp, $fp
# Load arguments into scratch registers
move $t0, $a0
move $t1, $a1
# set currMin = array[0]
lw $v0, 0($t0)
# Loop initialization
# i = 1
li $t2, 1
# while i < 5
MaxLoop:
slt $t3, $t2, $t1
beq $t3, $zero, MaxLoopDone
# Calculate element offset, store address in $t3
sll $t3, $t2, 2
add $t3, $t0, $t3
# Load array element at $t3, check if it's the new max
lw $t4, 0($t3)
sgt $t5, $t4, $v0
beq $t5, $zero, notGreater
# If so, set return value to it
move $v0, $t4
notGreater:
# i++
addi $t2, $t2, 1
j MaxLoop
MaxLoopDone:
# Unwind stack
lw $ra, -4($fp)
move $sp, $fp
lw $fp, -8($fp)
jr $ra
# int Min(array, length); result returned in $v0,
# No s or a registers are altered
FindMin:
# Backup return address and fp on stack.
sw $fp, 0($sp)
sw $ra, 4($sp)
addi $fp, $sp, 8
move $sp, $fp
# Load arguments into scratch registers
move $t0, $a0
move $t1, $a1
# set currMin = array[0]
lw $v0, 0($t0)
# Loop initialization
# i = 1
li $t2, 1
# while i < 5
MinLoop:
slt $t3, $t2, $t1
beq $t3, $zero, MinLoopDone
# Calculate element offset, store address in $t3
sll $t3, $t2, 2
add $t3, $t0, $t3
# Load array element at $t3, check if it's the new min
lw $t4, 0($t3)
slt $t5, $t4, $v0
beq $t5, $zero, notLesser
# If so, set return value to it
move $v0, $t4
notLesser:
# i++
addi $t2, $t2, 1
j MinLoop
MinLoopDone:
# Unwind stack
lw $ra, -4($fp)
move $sp, $fp
lw $fp, -8($fp)
jr $ra
As I mention in the code, the monkeying around with storing the $ra and $fp is a bit paranoid. If I was REALLY paranoid I'd store all the s registers as well. However, it's generally good practice and can save you a lot of headaches when you decide to add "function calls" in the middle of a function.
This is basically the approach to assembly where you write the program in C in your head and then translate that rather literally into assembly. Hence why I treat scaling, finding the max, etc as "functions".
A couple assemblyish notes: I repeatedly use sll $register, $register, 2 instead of multiplying by 4 (word size on MIPS32). This is because doing this is fewer instructions due to not having to cal li followed by mult followed by mflo. You can do it that way, and I used to, but once you get used to using sll and other bit fiddling operations a lot it's just cleaner to use sll as well as easier to read.
I do use a few pseudoinstructions, it's not a big deal, most modern MIPS assemblers and simulators that I'm aware of support them (including SPIM). I dance dangerously with the line after branch instructions (which are theoretically always executed regardless of the branch result), but in this case it wouldn't usually matter and most simulators and assemblers usually inject a no-op for you when it does anyway.
I know assembly can be a bit of a pain to read, so feel free to ask if you have any questions.
I can understand and use Java/c++ to a good extent, but for the life of me assembly just confuses me there are 2 functions I'm having trouble with. First:
One function that receives a string and prints it on the terminal
And another one that receives a string and converts it to integers (Strings given all made of numbers).
Any idea on where to start?
Update
On the second function, so far I got this:
main:
atoi:
li $v0, 8
la $a0, tstr
li $a1, 64
syscall
sub $sp, $sp,4
sw $ra, 0($sp)
move $t0, $a0
li $v0, 0
next:
lb $t1, ($t0)
beqz $t1, endloop
mul $v0, $v0, 10
add $v0, $v0, $t1
sub $v0, $v0, 48
add $t0, $t0, 1
b next
endloop:
lw $ra, 0($sp)
add $sp, $sp, 4
Updated code, still getting the error on 10 being an invalid operand. And about sub $v0, $v0, 48 should I just do it as sub $t1, $t1, 48?
For input-output stuff, you have to use system calls. For writing (zero-terminated) strings you'll use syscall #4, which wants the address of the buffer in $a0. Now just place the umber of the syscall in $v0 and execute it. For example, this snippet reads a string:
li $v0, 8 # syscall number
la $a0, theString # buffer
li $a1, 64 # length of buffer
syscall # read!
Here you can find some syscalls numbers.
For the second exercise, here's the C++ code, try to translate it :P
int result = 0;
while (*digit >= '0' && *digit <='9') { // digit is char *
result = (result * 10) + (*digit - '0');
digit++;
}
EDIT:
Ok, there are a couple of errors. First, you're not checking whether you've reached the end of the string (simply compare $t1 with 0 at the beginning). And you should first subtract '0' from $t1, then add it to $v0.
next:
lb $t1, ($t0)
# are there other character or this is the last one?
mul $v0, $v0, 10
add $v0, $v0, $t1
sub $v0, $v0, 48 # subtract 48 only from $t1, not from whole result
add $t0, $t0, 1
b next