Reputation: 1
I am trying to create an array that takes any number of single digit integers and when the user enters -1, it stops taking more. However, the first and second numbers are stored correctly. Once I syscall for a third input, the memory address where my second number is stored is overwritten. Here is my code in MIPS.
# switch to the Data segment
.data
# global data is defined here
myArray:
.word 256
char:
.word 4
sp:
.asciiz " "
cr:
.asciiz "\n"
error_string:
.asciiz "\ONE DOES NOT SIMPLY WALK INTO MORDOR."
array_input:
.asciiz "\Please type a digit. Press enter after each. End array with -1 input.\n"
neg_one:
.asciiz "\1-"
# switch to the Text segment
.text
.globl main
main:
# the rest of the main program goes here
lui $s7, 0x000a # set first half of $s7
ori $s7, $s7, 0x312d # set $s7 to compare with beq and exit input, $s7 = -1 now.
addi $t3, $t3, 0 # set counter for decrementing array later
la $s1, myArray # set base address of array to $s1
la $a0, array_input
jal Print_string
input_loop:
la $a0, char
li $a1, 4
jal Input # prompt user for digit input
lb $t1, char
lw $t2, char # store char from buffer into t1 (does this strip newline?)
beq $t2, $s7, begin_sort # branch if input is equivalent to -1
blt $t1, 48, error # check if char is not a digit (ascii<'0')
bgt $t1, 57, error # check if char is not a digit (ascii>'9')
addi $t1, $t1, -48
sw $t1, 0($s1) # store char into array
move $a0, $t1
jal Print_integer # print number that was input
la $a0, cr
jal Print_string # print newline char
addi $s1, $s1, 4 # increment array address
addi $t3, $t3, 1 # increment array counter
j input_loop # jump back up when -1 not entered
begin_sort:
Jal Exit
.globl Input
Input: # gets a string from user into register
addi $v0, $zero, 8
syscall #calls for input
jr $ra
.globl Print_integer
Print_integer: # print the integer in register a0. Loading one into $v0 from addi makes syscall print
addi $v0, $zero, 1
syscall
jr $ra
.globl Print_string
Print_string: # print the string whose starting address is in register a0
addi $v0, $zero, 4
syscall
jr $ra
.globl Exit
Exit: # end the program, no explicit return status
addi $v0, $zero, 10
syscall
jr $ra
For example, I type in 8, then 5, then 3 and for my array Address 0x10010000 Value (+0) is 0x08, Value (+4) is 0x0a, Value (+8) is 0x03. Obviously (+4) is wrong and should just be 0x05. It used to be that until Jal Input did Syscall. So I think something is wrong with my address at char? I cannot enter single digits after this because my code starts getting funky. I am also aware I could use sb, but I believe I need to store these are 4 bytes for now for later manipulation. Essentially, what is overwriting my memory address at Value (+4)?Thank you so much for any input here.
Upvotes: 0
Views: 715
Reputation: 33601
You were using the .word
directive incorrectly:
myArray: .word 256
This does not define an array with 256 elements. It defines a single scalar word with initial value of 256. The size of the array is only 4 bytes.
So, after the first store into the array, you are writing past the end (i.e. undefined behavior in C parlance).
In your answer, you moved myArray
to the end of the .data
section. But, this is not a fix. It merely "papers over" the problem. And, if you have the order myArray | char
, then the second element of myArray
will contain garbage data.
The correct way to define an array is to use the .space
directive. It reserves a given number of bytes. So, the correct definition for myArray
would be:
myArray: .space 1024
The char
array had a similar problem, but you "lucked out".
There were also a few more bugs as well. Particularly, the "-1" check was weak, if it worked at all.
I've created three versions of your program. A version with the bugs annotated. A second that is cleaned up and fixed. A third that is a bit simpler, more general, and more efficient because it uses a different syscall
to get user input.
Here is the annotated version [please pardon the gratuitous style cleanup]:
# switch to the Data segment
.data
# global data is defined here
# NOTE/BUG: this defines a _single_ 32 bit word [with initial value of 256]
# and _not_ an array with 256 elements
# as it is, myArray is only 4 bytes long
myArray: .word 256
# NOTE/BUG: this has similar problems to above but you luck out because
# it defines an area of length 4 by virtue of the .word directive and _not_
# the 4
char: .word 4
sp: .asciiz " "
cr: .asciiz "\n"
error_string: .asciiz "\ONE DOES NOT SIMPLY WALK INTO MORDOR."
array_input: .asciiz "\Please type a digit. Press enter after each. End array with -1 input.\n"
# NOTE/BUG: this string is incorrect for its intended use below
# NOTE/BUG: this isn't used
neg_one: .asciiz "\1-"
# switch to the Text segment
.text
.globl main
# the rest of the main program goes here
main:
# NOTE/BUG: this is a poor way to initialize this. use "li" or "lw" instead
# but also see below
# NOTE/BUG: manually compensating for little endian is tedious
lui $s7,0x000a # set first half of $s7
ori $s7,$s7,0x312d # set $s7 to compare with beq and exit input, $s7 = -1 now.
# NOTE/BUG: this has _no_ effect
addi $t3,$t3,0 # set counter for decrementing array later
la $s1,myArray # set base address of array to $s1
# NOTE/BUG: although not a bug, this should be part of the loop
la $a0,array_input
jal Print_string
input_loop:
la $a0,char
li $a1,4
jal Input # prompt user for digit input
lb $t1,char
# NOTE/BUG: this is a weak way to check for -1
lw $t2,char # store char from buffer into t1 (does this strip newline?)
beq $t2,$s7,begin_sort # branch if input is equivalent to -1
blt $t1,48,error # check if char is not a digit (ascii<'0')
bgt $t1,57,error # check if char is not a digit (ascii>'9')
addi $t1,$t1,-48
sw $t1,0($s1) # store char into array
move $a0,$t1
jal Print_integer # print number that was input
la $a0,cr
jal Print_string # print newline char
addi $s1,$s1,4 # increment array address
addi $t3,$t3,1 # increment array counter
j input_loop # jump back up when -1 not entered
begin_sort:
# NOTE/BUG: this _must_ be "jal" and _not_ "Jal"
# NOTE/BUG: this should just be "j" or just move the "Exit" code here
Jal Exit
error:
la $a0,error_string
li $v0,4
syscall
j input_loop
.globl Input
# gets a string from user into register
Input:
addi $v0,$zero,8
syscall # calls for input
jr $ra
.globl Print_integer
# print the integer in register a0. Loading one into $v0 from addi makes syscall print
Print_integer:
addi $v0,$zero,1
syscall
jr $ra
.globl Print_string
# print the string whose starting address is in register a0
Print_string:
addi $v0,$zero,4
syscall
jr $ra
.globl Exit
# end the program, no explicit return status
Exit:
addi $v0,$zero,10
syscall
jr $ra
Here is the cleaned up and working version. Note that the best way to do the "-1" check would be to strip the newline and implement a strcmp
function, but I did something simpler
# switch to the Data segment
.data
# global data is defined here
myArray: .space 1024
eArray:
char: .space 80
echar:
sp: .asciiz " "
cr: .asciiz "\n"
error_string: .asciiz "\ONE DOES NOT SIMPLY WALK INTO MORDOR."
array_input: .asciiz "\Please type a digit. Press enter after each. End array with -1 input.\n"
# switch to the Text segment
.text
.globl main
# the rest of the main program goes here
main:
la $s1,myArray # set base address of array to $s1
la $s2,eArray # get end of array
input_loop:
# prompt user
la $a0,array_input
jal Print_string
# read in user's response
# NOTE: unless you are _required_ to decode the number yourself, using
# syscall 5 (read integer) is _much_ simpler
la $a0,char
la $a1,echar
subu $a1,$a1,$a0
jal Input # prompt user for digit input
# check for -1 on input
lb $t2,0($a0) # is first char "-"?
bne $t2,0x2D,not_negone # no, fly
lb $t2,1($a0) # is second char "1"?
bne $t2,0x31,not_negone # no, fly
lb $t2,2($a0) # is third char "\n"?
beq $t2,0x0A,begin_sort # yes, fly
not_negone:
lb $t1,0($a0)
blt $t1,48,error # check if char is not a digit (ascii<'0')
bgt $t1,57,error # check if char is not a digit (ascii>'9')
addi $t1,$t1,-48
sw $t1,0($s1) # store char into array
move $a0,$t1
jal Print_integer # print number that was input
la $a0,cr
jal Print_string # print newline char
addi $s1,$s1,4 # increment array address -- over edge?
blt $s1,$s2,input_loop # no, loop
begin_sort:
j Exit
error:
la $a0,error_string
li $v0,4
syscall
j input_loop
# gets a string from user into register
Input:
li $v0,8
syscall # calls for input
jr $ra
# print the integer in register a0. Loading one into $v0 from addi makes syscall print
Print_integer:
li $v0,1
syscall
jr $ra
# print the string whose starting address is in register a0
Print_string:
li $v0,4
syscall
jr $ra
# end the program, no explicit return status
Exit:
li $v0,10
syscall
The above program still suffers from a limitation in that the numbers input can only be single digits. The general solution would be to implement the equivalent of atoi
.
But, if you're not required to do your own number parsing, using syscall
#5 [read integer] is much simpler, allows any integer, and is what most mips programs use.
# switch to the Data segment
.data
# global data is defined here
myArray: .space 1024
eArray:
sp: .asciiz " "
cr: .asciiz "\n"
error_string: .asciiz "\ONE DOES NOT SIMPLY WALK INTO MORDOR."
array_input: .asciiz "\Please type a number. Press enter after each. End array with -1 input.\n"
# switch to the Text segment
.text
.globl main
# the rest of the main program goes here
main:
la $s1,myArray # set base address of array to $s1
la $s2,eArray # get end of array
input_loop:
# prompt user
la $a0,array_input
jal Print_string
# read in user's response
li $v0,5
syscall
move $t1,$v0
# check for -1 on input
bltz $t1,begin_sort
sw $t1,0($s1) # store char into array
move $a0,$t1
jal Print_integer # print number that was input
la $a0,cr
jal Print_string # print newline char
addi $s1,$s1,4 # increment array address -- over edge?
blt $s1,$s2,input_loop # no, loop
begin_sort:
j Exit
error:
la $a0,error_string
li $v0,4
syscall
j input_loop
# gets a string from user into register
Input:
li $v0,8
syscall # calls for input
jr $ra
# print the integer in register a0. Loading one into $v0 from addi makes syscall print
Print_integer:
li $v0,1
syscall
jr $ra
# print the string whose starting address is in register a0
Print_string:
li $v0,4
syscall
jr $ra
# end the program, no explicit return status
Exit:
li $v0,10
syscall
Upvotes: 0
Reputation: 1
Solution?: Wow, I solved this issue by literally moving the myArray and char part below the declaration of the strings, like sp and cr. I think this is because the array space should be declared after the strings, otherwise the strings will overwrite whatever space you just declared. Then, as I adjusted my dynamic array, I ruined the strings that were stored. Weird stuff. I'd still appreciate a more full explanation.
Upvotes: 0