So here the code as wished:
PROGRAM 1
.text
.globl main
main:
li $t0, 10
mtc1 $t0, $f12
cvt.s.w $f12, $f12 # 10.0 as single in $f12
jal printFloat
li $v0, 4001 #sys_exit
syscall
printFloat:
addi $sp, $sp, -4 #opens the stack frame
sw $ra, 0($sp) #saves the return adress
cvt.d.s $f12, $f12 #converts the single to double
la $a0, strDouble #arguments needed for printf ("%f" in $a0, upper 32 bit of the float in $a2, lower ones in $a1)
mfc1 $a1, $f12
mfc1 $a2, $f13
jal printf
jal fflush
la $a1, strBreakLine #arguments needed for printf (Adress of String in $a1, "%s" in $a0)
la $a0, strStringOut
jal printf
jal fflush
lw $ra, 0($sp) #restores the return adress
addi $sp, $sp, 4 #pops the stack frame
jr $ra
.data
strDouble: .asciiz "%f"
strStringOut: .asciiz "%s"
strBreakLine: .asciiz "\n"
phm15fix@ci20:~$ gcc -o test -g test1.s
phm15fix@ci20:~$ ./test
10.000000
PROGRAM 2
.text
.globl main
main:
li $t0, 10
mtc1 $t0, $f12
cvt.s.w $f12, $f12 # 10.0 as single in $f12
jal printFloat
li $v0, 4001 #sys_exit
syscall
printFloat:
addi $sp, $sp, -4 #opens the stack frame
sw $ra, 0($sp) #saves the return adress
cvt.d.s $f12, $f12 #converts the single to double
la $a0, strDouble #arguments needed for printf ("%f" in $a0, upper 32 bit of the float in $a2, lower ones in $a1)
mfc1 $a1, $f12
mfc1 $a2, $f13
jal printf
jal fflush
jal printNewLine
lw $ra, 0($sp) #restores the return adress
addi $sp, $sp, 4 #pops the stack frame
jr $ra
printNewLine:
addi $sp, $sp, -4 #opens the stack frame
sw $ra, 0($sp) #saves the return adress
la $a1, strBreakLine #arguments needed for printf (Adress of String in $a1, "%s" in $a0)
la $a0, strStringOut
jal printf
jal fflush
lw $ra, 0($sp) #restores the return adress
addi $sp, $sp, 4 #pops the stack frame
jr $ra
.data
strDouble: .asciiz "%f"
strStringOut: .asciiz "%s"
strBreakLine: .asciiz "\n"
phm15fix@ci20:~$ gcc -o test -g test1.s
phm15fix@ci20:~$ ./test
10.000000
Bus error
PROGRAM 3
.text
.globl main
main:
li $t0, 10
mtc1 $t0, $f12
cvt.s.w $f12, $f12 # 10.0 as single in $f12
jal function
li $v0, 4001 #sys_exit
syscall
function:
addi $sp, $sp, -4 #opens the stack frame
sw $ra, 0($sp) #saves the return adress
jal printFloat
lw $ra, 0($sp) #restores the return adress
addi $sp, $sp, 4 #pops the stack frame
jr $ra
printFloat:
addi $sp, $sp, -4 #opens the stack frame
sw $ra, 0($sp) #saves the return adress
cvt.d.s $f12, $f12 #converts the single to double
la $a0, strDouble #arguments needed for printf ("%f" in $a0, upper 32 bit of the float in $a2, lower ones in $a1)
mfc1 $a1, $f12
mfc1 $a2, $f13
jal printf
jal fflush
jal printNewLine
lw $ra, 0($sp) #restores the return adress
addi $sp, $sp, 4 #pops the stack frame
jr $ra
printNewLine:
addi $sp, $sp, -4 #opens the stack frame
sw $ra, 0($sp) #saves the return adress
la $a1, strBreakLine #arguments needed for printf (Adress of String in $a1, "%s" in $a0)
la $a0, strStringOut
jal printf
jal fflush
lw $ra, 0($sp) #restores the return adress
addi $sp, $sp, 4 #pops the stack frame
jr $ra
.data
strDouble: .asciiz "%f"
strStringOut: .asciiz "%s"
strBreakLine: .asciiz "\n"
At the end of every program is the specific output.
The first program works fine. In the second program I made an extra function for printing a new line. If I run this I get a "bus error".
And in the third program I made a dummy function to simulate another stacklevel. There is also a "bus error" and the number i want to print is not printing.
I think there are some problem with our stack.
I used the debugger and if I load my return-address from stack I have the wrong address in my $ra and i dont know why. If it jumps to this address i get the "bus error".
Here are the functions:
PRINT_DOUBLE:
addi $sp, $sp, -4
sw $ra, 0($sp)
la $a0, strDouble
mfc1 $a1, $f12
mfc1 $a2, $f13
jal printf
nop
# lw $a0, stdout
# jal fflush
jal BREAK_LINE
lw $ra, 0($sp)
addi $sp, $sp, 4
jr $ra
BREAK_LINE:
addi $sp, $sp, -4
sw $ra, 0($sp)
la $a0, strBreakLine
jal PRINT_STRING
lw $ra, 0($sp)
addi $sp, $sp, 4
nop
jr $ra
PRINT_STRING:
addi $sp, $sp, -4
sw $ra, 0($sp)
add $a1, $a0, $zero
la $a0, strStringOut
jal printf
la $a0, stdout
lw $a0, 0($a0)
jal fflush
lw $ra, 0($sp)
addi $sp, $sp, 4
nop
jr $ra
I've looked over your program and most of it looks fine. I think your stack save/restore is fine. But, I see at least one other problem.
After every jal printf
, you're doing an immediate jal fflush
, so fflush
will get printf's first argument [which is a string pointer] instead of a file descriptor pointer (e.g. stdout
).
Under the mips ABI, the $a0-$a3
registers may be modified/destroyed by the callee. So, after printf
returns $a0
can be anything.
All three programs seem to have this problem. IMO, if program 1 is working, it's just the luck of the draw (i.e.) whatever ends up in $a0
is harmless enough. That is, whatever is in it, points to a memory location that isn't a file descriptor but fflush
tries to interpret it as one and lucks out.
Also, for fflush
, $a0
should point to an address that is word [4 byte] aligned. If it's not, that could be the source of the bus error.
To fix this, change all fflush
calls to:
lw $a0,stdout
jal fflush
That should probably work, but, depending on what the gcc
assembler does, you may have to do:
la $a0,stdout
lw $a0,0($a0)
jal fflush
I've seen that the buserror appears if I try to jump back from a function For example I jump to PRINT_DOUBLE, there I jump to BREAK_LINE and there I jump to PRINT_STRING If I jump then back to BREAK_LINE all is fine, but back from BREAK_LINE to PRINT_DOUBLE I get the buserror
I've desk checked your code and [once again] seems fine. One way to debug this is to [using gdb
] is to single step [with stepi
] within your functions and put breakpoints after the the jal printf
[or jal fflush
].
Before and after each jal
, note the $sp
value. It must be the same. Do this for all your functions. Also, when returning from a function, note the $sp
value, then the value from the lw
[which goes into $ra
]. They should all match the "expected"
Further, $sp
must be 4 byte aligned at all times. Actually, according to a mips ABI document I've seen [possibly dated], it says that stack frames must be 8 byte aligned. That may be overkill at this point, but I mention it.
If you do lw
from unaligned, that's an alignment exception, which will probably show up as SIGBUS
. Further, check the $ra
value [before doing the jr $ra
]. It also must be the "expected" value and be 4 byte aligned.
In other words, exactly which instruction produces the exception?
Another thing you can do is to comment out some function calls. Start with the jal fflush
. Then, comment out jal printf
[in your sub-functions]. I note that you do a "naked" printf
call at the outset, which seems fine. Keep doing this until the program stops faulting. This ought to help localize the area/call.
You didn't state whether you were running this in a simulator like spim
or mars
or real H/W [possibly running linux]. I suspect real H/W. But, you could verify your logic by running under a simulator [I prefer mars
] and provide dummy functions for printf
and fflush
that just do jr $ra
. Note that neither spim
nor mars
can link to .o
files. They work purely from source, so you can only use your .s
If you're running on real H/W, gdb
ought to be able to provide detailed information on the source of the exception. If not, create a C function that sets up a signal handler for SIGBUS
using sigaction
[see the manpage]. Then, put a breakpoint on the signal handler.
One of the arguments to the signal handler is a pointer to a siginfo_t
struct that has additional information. Note that for SIGBUS
, the si_code
field will have a BUS_*
value that may have more information.The third argument, although void *
is a pointer to something that can give you the register values at the time of the exception.
In another answer I gave: mips recursion how to correctly store return address for a function another OP had a similar problem. My answer added some special stack alignment and check code that may give you some ideas.