confused_student
confused_student

Reputation: 1

MIPS, dynamic array, third input overwrites the second

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

Answers (2)

Craig Estey
Craig Estey

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

confused_student
confused_student

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

Related Questions