Assembly Call Subprograms Based on User Input

Assembly call subprograms based on user input

cmp %esp, '0' is wrong, because it tries to compare the value of %esp to the value in memory at address '0'. At&t syntax uses reversed operands, and it needs a $ prefix for immediates. But you already know this, I guess you were just a little careless. The correct instruction is cmpb $'0', (%esp) to compare the byte in memory at address %esp to the ascii code of 0.

Furthermore, you allocated 4 bytes from the stack, but you never free that. When you eventually hit a ret it will use your local variable as return address which is of course a bad thing :) A nice trick is to use lea 4(%esp), %esp to free it without affecting the flags, so you can do this between the cmp and the jz. If you like less tricky stuff, you can of course just pop the input into a register and use that in the comparison, such as:

pop %eax
cmp $'0', %al

PS: Learn to use a debugger. That would have pointed you directly at the instruction, and then you probably could have figured out the problem yourself.

Subroutine being being called, but something wrong with arithmetic?

Answer 2, containing reviewed the updated source from OP (after he fixed the problems with original source):

Just adjusted source, how I would expect it to be written:

# register allocation through main code:
# s0 = u, s1 = v, s2 = intermediate/final result

# WARNING: code needs delayed branching ON (like real MIPS CPU)

.text
.globl main
main:

li $v0, 4 # print string syscall method
la $a0, prompt # prints prompt
syscall

# input "u"
li $v0, 4 # print string syscall method
la $a0, prompt1 # prints "u" prompt
syscall

li $v0, 5 # reads the int typed
syscall
move $s0, $v0 # remember "u" input

# input "v"
li $v0, 4 # print string syscall method
la $a0, prompt2 # prints "v" prompt
syscall

li $v0, 5 # reads the int typed
syscall
move $s1, $v0 # remember "v" input

# result = 0
move $s2, $zero

# exit when user did enter pair of zeroes
or $at, $s0, $s1 # binary OR(u, v)
bne $at, $zero, doCalculation # when both inputs are zero -> terminate
li $v0, 10 # nop not needed, v0=10 is harmless in case of calculation
syscall # terminate

doCalculation:
# calculate result += 7*u*u
move $a1, $s0 # a1 = u
move $a2, $s0 # a2 = u
jal arithmetic # jumps to subroutine
li $a0, 7 # load a0 in delayed branching as argument for subroutine
add $s2, $s2, $v0 # update result

# calculate result += -25*u*v
move $a1, $s0 # a1 = u
move $a2, $s1 # a2 = v
jal arithmetic # jumps to subroutine
li $a0, -25 # load a0 in delayed branching as argument for subroutine
add $s2, $s2, $v0 # update result

# calculate result += 63*v*v
move $a1, $s1 # a1 = v
move $a2, $s1 # a2 = v
jal arithmetic # jumps to subroutine
li $a0, 63 # load a0 in delayed branching as argument for subroutine
add $s2, $s2, $v0 # update result

# display result
li $v0, 4
la $a0, prompt3
syscall

li $v0, 1
move $a0, $s2
syscall

li $v0, 4
la $a0, newline
syscall

j main # rinse and repeat until [0, 0] user input
nop # needed, to not execute slow MUL from subroutine

arithmetic:
mul $v0, $a1, $a2
jr $ra
mul $v0, $a0, $v0 # v0 = a0 * a1 * a2

.data
prompt: .asciiz "Type zero for both u and v to end program\n"
prompt1: .asciiz "Please type value for 'u': \n"
prompt2: .asciiz "Please type value for 'v': \n"
prompt3: .asciiz "Result is: "
newline: .asciiz "\n"

Call a subroutine on each character in string - MIPS

You will need to save $a0 and $a1 elsewhere (usually stack) for the duration of the subroutine call. Also, your loop isn't a subroutine, no reason to call it using jal. On the other hand, the callback is a subroutine, you should call it using jalr. Something along these lines should work:

string_for_each:

addiu $sp, $sp, -12 # Need 3 locals for $a0, $a1 and $ra
sw $ra, 0($sp) # Store $ra
sw $a1, 8($sp) # Store $a1

loop:
sw $a0, 4($sp) # Store $a0 as it will be used for argument
lb $t0, 0($a0) # Get current character
beq $t0, $zero, end_for_each # Done when reaching NULL character
jalr $a1 # Call callback subroutine
lw $a0, 4($sp) # Reload $a0
lw $a1, 8($sp) # $a1 could have changed (calling convention)
addi $a0, $a0, 1 # Increment to get next character in string
j loop

end_for_each:
lw $ra, 0($sp) # Reload return address to caller
addiu $sp, $sp, 12 # Free locals
jr $ra

x86 assembly: Pass parameter to a function through stack

Typically, you use the base pointer (bp on 16 bit, ebp on 32 bit) to refer to parameters and locals.

The basic idea is that every time you enter into a function you save the stack pointer inside the base pointer, to have the stack pointer at when the function was called as a "fixed reference point" throughout execution of the function. In this schema [ebp-something] typically is a local, [ebp+something] is a parameter.

Transposing the typical 32-bit, callee-cleanup calling conventions you can do like this:

caller:

push param1
push param2
call subroutine

subroutine:

push bp       ; save old base pointer
mov bp,sp ; use the current stack pointer as new base pointer
; now the situation of the stack is
; bp+0 => old base pointer
; bp+2 => return address
; bp+4 => param2
; bp+6 => param1
mov ax,[bp+4] ; that's param2
mov bx,[bp+6] ; that's param1
; ... do your stuff, use the stack all you want,
; just make sure that by when we get here push/pop have balanced out
pop bp ; restore old base pointer
ret 4 ; return, popping the extra 4 bytes of the arguments in the process


Related Topics



Leave a reply



Submit