views:

370

answers:

2

I am trying to have the function vbsme call another function called sad... is the following procedure correct about saving the registers and return address?? the caller is supposed to save register $t0-$t7, but where and how should I do that?

vbsme: li $v0, 0   # reset $v0 
     li $v1, 0   # reset $v1
     li  $t0, 1   # i(row) = 1 
     li  $t1, 1   # j(col) = 1
     lw  $t2, 0($a0)  # row size
     lw  $t3, 4($a0)  # col size
     mul  $t4, $t2, $t3  # row * col
     li  $t5, 0   # element = 0
    loop:   bgeq  $t5, $t4, exit  # if element >= row * col then exit

     addi $sp, $sp, -16  # create space on the stack pointer
     sw $ra, -12($sp)  # save return address
     sw $s6, -8($sp)  # save return address
     sw $s7, -4($sp)  # save return address
     subi $s7, $t0, 1  # 1st parameter: i-1
     subi $s6, $t1, 1  # 2nd parameter: j-1
     jal  sad   # calculate the sum of absolute difference using the frame starting from row a0 and col a1
     lw $ra, -12($sp)  # restore return address
     lw $s6, -8($sp)  
     lw $s7, -4($sp)
     addi $sp, $sp, 16  # restore stack pointer
     jr $ra
+1  A: 

$sx registers are guaranteed to be unchanged accross function calls, so its the callee (sum function) the responsible of saving them, only if its going to change their value.

$tx registers, on the other hand, are not guaranteed to be unchanged over function calls, so its the responsability of the caller (vbsme) to save them.

You should save $sx in the callee stack.

So when you start coding the sum function, you should save space in the stack If you want to save n registers, then save n*4.

Space in the stack is saved by subtracting on the $sp register, which points to the base of the stack. Before your function code, you should create the stack for that function, saving all caller-saved registers, return address and global pointer registers when neccesary

sum:
       #stack frame creation. Caller registers saved, 
       # return address and frame pointer

       subu $sp,$sp,36 #Save space in the stack for registers $s0, $s7 + $ra
       sw   $ra,32($sp)
       sw   $s0,0($sp)
       sw   $s1,4($sp)
       #and so on. Note that also you should save the $ra register only if you are
       # going to call another function

       #do something with $sx  

       #stack frame destruction
       #restore $sx and $ra registers
       lw  $ra,32($sp)
       lw  $s0,0($sp)
       lw  $s1,4($sp)
       ...
       lw  $s7,28($sp)

        jr $ra

By the way, by convention, registers $a0, $a3 should keep the arguments to the function you are calling. Also, note that because you are using the $s0, $s7 registers, you have to do some extra work. Convention says that if you don't use them, then you shouldn't save them, so maybe you could use the $tx (temporary) registers instead.

Tom
shouldn't the caller save register $t0-$t7 instead of $s registers?
EquinoX
vbsme here is the caller to the sum function right?
EquinoX
vbsme caller, sum callee
Tom
what if after the sum function vbsme needs to do some more instructiosn, where does that fit?
EquinoX
so if I save $sx in the sum function then I should use s registers in the vbsme as well?? how do I save the $tx registers?
EquinoX
You should save the $sx registers in the sum function ONLY if you are going to use them in the sum function. No saving needed if sum function is not going to save them. To save the $tx registers (vbsme should do it), you should save space in the stack, and store them in the vbsme stack, as shown for the $sx registers in the sum function
Tom
+1  A: 

Alexander,

What Tom said is pretty much correct the important thing to note with doing programming in assembly is everything is by convention. While in MIPS the common convention is what Tom noted it is not the only convention. For instance if you are using macro's (which if you are going to be writing more than 1 or 2 functions in assembly it is a much easier to use macros) then you can define your calling convention in the macro. The easiest way to do this is instead of having the callee save the stack is to have the caller save the stack. This is less efficient because sometimes (perhaps many times) unused registers will be saved, however it will save you a lot of heartache because your convention is applied consistently.

Caller Stack Save:

    sw $fp 0($sp) # save the old frame pointer
    addu $fp $sp $0 # move the frame pointer to point at top of frame
    subu $sp $sp 44 # move the stack pointer down 44
    sw $fp 40($sp) # save the old stack pointer
    sw $ra 36($sp) # save the return address
    sw $s0 32($sp) # save registers $s0 - $s7
    sw $s1 28($sp)
    sw $s2 24($sp)
    sw $s3 20($sp)
    sw $s4 16($sp)
    sw $s5 12($sp)
    sw $s6 8($sp)
    sw $s7 4($sp)

Function call

    jal my_func

Caller Stack Restore

    subu $sp $fp 44 # move the stack pointer to the orginal unmodified bottom
    lw $ra 36($sp) # load the return address
    lw $s0 32($sp) # load registers $s0 - $s7
    lw $s1 28($sp)
    lw $s2 24($sp)
    lw $s3 20($sp)
    lw $s4 16($sp)
    lw $s5 12($sp)
    lw $s6 8($sp)
    lw $s7 4($sp)
    lw $fp 44($sp) # load the old frame pointer
    lw $sp 40($sp) # load the old stack pointer

Your function:

my_func:
    do some stuff
    jr $ra             # return to the previous function

As I said the best way to apply this convention is to use macros. I did a project in SPIM (which you may being using or you may not be) for a class. As part of the project we wrote a macro engine (it does other cool stuff as well) for SPIM you can get it here: http://github.com/timtadh/mpp I would also recommend checking out http://github.com/timtadh/jist which is a toy operating system written on top of SPIM. It will give you a sense of how to use the macro engine.

cheers

tim.tadh