1

I am new to asking questions on stack overflow, and even newer to MIPS. I tried implement the following recursive solution to the 'hanoi towers' problem (taken from here) using MIPS. I think my failure in doing so lies behind the fact that I don't understand when I should restore/save registers such as $ra, $sp - so I just assume I need to do so everywhere - any help would be greatly appreciated:

void towerOfHanoi(int n, char from_rod, char to_rod, char aux_rod)
{
    if (n == 1)
    {
        printf("\n Move disk 1 from rod %c to rod %c", from_rod, to_rod);
        return;
    }
    towerOfHanoi(n-1, from_rod, aux_rod, to_rod);
    printf("\n Move disk %d from rod %c to rod %c", n, from_rod, to_rod);
    towerOfHanoi(n-1, aux_rod, to_rod, from_rod);
}

int main()
{
    int n = 4; // Number of disks
    towerOfHanoi(n, 'A', 'C', 'B');  // A, B and C are names of rods
    return 0;
}

And this is my attempt to 'translate' it into MIPS:

.globl main
main:
    addi $sp, $sp, -4
    sw $ra, 0($sp)

    li $v0, 5 # system call code for read_int
    syscall

    move $a0, $v0 # a0 holds the number of disks
    li $a1, 'A' # a1 holds from_rod
    li $a2, 'C' # a2 holds to_rod
    li $a3, 'B' # a3 holds aux_rod

    jal hanoi

    lw $ra, 0($sp)
    addi $sp, $sp, 4
    li $v0, 10
    syscall

hanoi:
    addi $sp, $sp, -4
    sw $ra, 0($sp)

    beq $a0, 1, base_case
    addi $a0, $a0, -1 # number_of_disks -= 1
    # swap between to_rod and aux_rod :
    move $t0, $a2 # temp = to_rod
    move $a2, $a3 # to_rod = aux_rod
    move $a3, $t0 # aux_rod = temp
    jal hanoi

    jal print

    # swap between from_rod and aux_rod :
    move $t0, $a1 # temp = from_rod
    move $a1, $a3 # from_rod = aux_rod
    move $a3, $t0 # aux_rod = temp
    jal hanoi

    lw $ra, 0($sp)
    addi $sp, $sp, 4
    jr $ra
base_case:
    addi $sp, $sp, -4
    sw $ra, 0($sp)

    jal print

    lw $ra, 0($sp)
    addi $sp, $sp, 4
    jr $ra
print:
    addi $sp, $sp, -4
    sw $ra, 0($sp)
    move $t0, $a0

    li $v0, 11
    move $a0, $a1
    syscall # print from_rod
    li $a0, '>'
    syscall # print the char '>'
    move $a0, $a2
    syscall # print  to_rod
    li $a0, '\n'
    syscall # pirnt new line
    move $a0, $t0

    lw $ra, 0($sp)
    addi $sp, $sp, 4
    jr $ra
Marko Klei
  • 11
  • 1
  • 2
  • 1
    Does something bad happen when you run this? What did you learn when you ran it under the debugger? – David Wohlferd May 17 '18 at 02:47
  • In your C version, notice how the 2nd call (`towerOfHanoi(n-1, aux_rod, to_rod, from_rod);`) uses the same variables as the first function call. i.e. your function args are still needed after the first recursive call, so you have to save / restore them, too. Look at compiler output for your C version (https://godbolt.org/g/z7Pnp8) if you want to see one way to do it. – Peter Cordes May 17 '18 at 03:13
  • @DavidWohlferd It does run, just produces the wrong output. – Marko Klei May 17 '18 at 03:58
  • @PeterCordes I think I understand what you mean - the 2nd call is essentially using the parameters after all the manipulation they went through in the first call. I checked out the link you put here, unfortunately, couldn't understand much of it - thank you nonetheless. – Marko Klei May 17 '18 at 04:01
  • 1
    @MarkoKlei: yes, but *what* wrong output: show it to make this a [mcve]. – Peter Cordes May 17 '18 at 04:28
  • Right, your asm is clobbering the function args you need for the 2nd `jal hanoi`. (Your `print` function probably also clobbers registers, so you need to reload before calling it, then reload again before the 2nd `jal`.) The gcc output I linked uses MIPS register numbers, not the human-friendly `$t0..$t9` names, see http://www.cs.uwm.edu/classes/cs315/Bacon/Lecture/HTML/ch05s03.html for the mapping. But the key point is to note how much stack space it reserves to save the function args and `$ra`, and when / where it does that saving. You can use `-fverbose-asm`: https://godbolt.org/g/DGQxje – Peter Cordes May 17 '18 at 04:30

2 Answers2

3

Have a look at this solution:

# Towers of Hanoi
# MIPS assembly implementation (tested with MARS)

.data
prompt: .asciiz "Enter a number: "
part1: .asciiz "\nMove disk "
part2: .asciiz " from rod "
part3: .asciiz " to rod "

.text
.globl main
main:
    li $v0,  4          # print string
    la $a0,  prompt
    syscall
    li $v0,  5          # read integer
    syscall

    # parameters for the routine
    add $a0, $v0, $zero # move to $a0
    li $a1, 'A'
    li $a2, 'B'
    li $a3, 'C'

    jal hanoi           # call hanoi routine

    li $v0, 10          # exit
    syscall

hanoi:

    #save in stack
    addi $sp, $sp, -20 
    sw   $ra, 0($sp)
    sw   $s0, 4($sp)
    sw   $s1, 8($sp)
    sw   $s2, 12($sp)
    sw   $s3, 16($sp)

    add $s0, $a0, $zero
    add $s1, $a1, $zero
    add $s2, $a2, $zero
    add $s3, $a3, $zero

    addi $t1, $zero, 1
    beq $s0, $t1, output

    recur1:

        addi $a0, $s0, -1
        add $a1, $s1, $zero
        add $a2, $s3, $zero
        add $a3, $s2, $zero
        jal hanoi

        j output

    recur2:

        addi $a0, $s0, -1
        add $a1, $s3, $zero
        add $a2, $s2, $zero
        add $a3, $s1, $zero
        jal hanoi

    exithanoi:

        lw   $ra, 0($sp)        # restore registers from stack
        lw   $s0, 4($sp)
        lw   $s1, 8($sp)
        lw   $s2, 12($sp)
        lw   $s3, 16($sp)

        addi $sp, $sp, 20       # restore stack pointer

        jr $ra

    output:

        li $v0,  4              # print string
        la $a0,  part1
        syscall
        li $v0,  1              # print integer
        add $a0, $s0, $zero
        syscall
        li $v0,  4              # print string
        la $a0,  part2
        syscall
        li $v0,  11             # print character
        add $a0, $s1, $zero
        syscall
        li $v0,  4              # print string
        la $a0,  part3
        syscall
        li $v0,  11             # print character
        add $a0, $s2, $zero
        syscall

        beq $s0, $t1, exithanoi
        j recur2

This is pretty much direct translation of your C code and should be enough to understand how to preserve routine arguments through stack.

Andriy Makukha
  • 7,580
  • 1
  • 38
  • 49
0

Makukha's MIPS program works on QtSPIM as well.

Here is another, and more visual recursive Towers of Hanoi program, written in CodeAPeel-C assembly language with a built-in pixel screen.

enter image description here

AYO
  • 11
  • 1